react-icons/ai#AiOutlineSearch TypeScript Examples

The following examples show how to use react-icons/ai#AiOutlineSearch. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx    From dxvote with GNU Affero General Public License v3.0 5 votes vote down vote up
Filter = () => {
  const { guild_id: guildId } =
    useParams<{ chain_name?: string; guild_id?: string }>();
  const [viewFilter, setViewFilter] = useState(false);
  const { totalFilters } = useFilter();

  const history = useHistory();
  const location = useLocation();

  const { account } = useWeb3React();
  const { data: votingPower } = useVotingPowerOf({
    contractAddress: guildId,
    userAddress: account,
  });
  const { data: guildConfig } = useGuildConfig(guildId);
  const isProposalCreationAllowed = useMemo(() => {
    if (!guildConfig || !votingPower) {
      return false;
    }
    if (votingPower.gte(guildConfig.votingPowerForProposalCreation)) {
      return true;
    }
    return false;
  }, [votingPower, guildConfig]);
  const [openSearchBar, setOpenSearchBar] = useState(false);
  return (
    <FilterContainer>
      <FilterRow>
        {isMobile && !isProposalCreationAllowed && (
          <FilterButton
            onClick={() => setViewFilter(!viewFilter)}
            active={viewFilter || totalFilters > 0}
          >
            Filter
            {totalFilters > 0 && <FilterBadge>{totalFilters}</FilterBadge>}
          </FilterButton>
        )}
        {isDesktop && <FilterMenu />}

        <ButtonContainer>
          <StyledIconButton
            variant="secondary"
            padding="0.4rem"
            onClick={() => setOpenSearchBar(!openSearchBar)}
          >
            <AiOutlineSearch size={20} />
          </StyledIconButton>
          {isProposalCreationAllowed && (
            <Button
              variant="secondary"
              onClick={() => history.push(location.pathname + '/proposalType')}
              data-testid="create-proposal-button"
            >
              Create Proposal
            </Button>
          )}
        </ButtonContainer>
      </FilterRow>
      {isMobile && viewFilter && <FilterMenu />}
      {openSearchBar ? (
        <StyledInputWrapper>
          <Input
            icon={<AiOutlineSearch size={24} />}
            placeholder="Search title, ENS, address"
          />
        </StyledInputWrapper>
      ) : null}
    </FilterContainer>
  );
}
Example #2
Source File: LandingPage.tsx    From dxvote with GNU Affero General Public License v3.0 5 votes vote down vote up
LandingPage: React.FC = () => {
  const { chainId } = useWeb3React();
  const chainName =
    getChains().find(chain => chain.id === chainId)?.name || null;

  const { data: allGuilds } = useGuildRegistry(
    configs[chainName].contracts.utils.guildRegistry
  );

  /*TODO:
    1. Members should be dynamic
    2. Amount of proposals should be dynamic
    3. Logo should be dynamic
    */
  return (
    <>
      <InputContainer>
        <Input
          icon={<AiOutlineSearch size={24} />}
          placeholder="Search Guild"
        />
        <StyledButton data-testid="create-guild-button">
          {' '}
          <StyledLink to={location => `${location.pathname}/createGuild`}>
            Create Guild
          </StyledLink>
        </StyledButton>
      </InputContainer>
      <CardContainer>
        {allGuilds
          ? allGuilds.map(guildAddress => (
              <GuildCard key={guildAddress} guildAddress={guildAddress}>
                <GuildCardHeader>
                  <MemberWrapper>
                    <MdOutlinePeopleAlt size={24} />
                    500
                  </MemberWrapper>
                  <ProposalsInformation proposals={'active'}>
                    4 Proposals
                  </ProposalsInformation>
                </GuildCardHeader>
                <GuildCardContent>
                  <DaoIcon src={dxDaoIcon} />
                  <Title guildAddress={guildAddress} />
                </GuildCardContent>
              </GuildCard>
            ))
          : null}
      </CardContainer>
    </>
  );
}
Example #3
Source File: ContactsLayout.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
ContactsLayout = (): JSX.Element => {
    const [search, setSearch] = useState('' as string);
    const [isLoading, setIsLoading] = useBoolean(true);
    const [contacts, setContacts] = useState([] as any[]);
    const [permission, setPermission] = useState((): string | null => {
        return null;
    });
    const dialogRef = useRef(null);
    const inputFile = useRef(null);
    const [dialogOpen, setDialogOpen] = useBoolean();
    const alertRef = useRef(null);
    const [requiresConfirmation, confirm] = useState((): string | null => {
        return null;
    });

    let filteredContacts = contacts;
    if (search && search.length > 0) {
        filteredContacts = filteredContacts.filter((c) => buildIdentifier(c).includes(search.toLowerCase()));
    }

    const {
        currentPage,
        setCurrentPage,
        pagesCount,
        pages
    } = usePagination({
        pagesCount: Math.ceil(filteredContacts.length / perPage),
        initialState: { currentPage: 1 },
    });

    const refreshPermissionStatus = async (): Promise<void> => {
        setPermission(null);
        await waitMs(500);
        ipcRenderer.invoke('contact-permission-status').then((status: string) => {
            setPermission(status);
        }).catch(() => {
            setPermission('Unknown');
        });
    };

    const requestContactPermission = async (): Promise<void> => {
        setPermission(null);
        ipcRenderer.invoke('request-contact-permission').then((status: string) => {
            setPermission(status);
        }).catch(() => {
            setPermission('Unknown');
        });
    };

    const loadContacts = (showToast = false) => {
        ipcRenderer.invoke('get-contacts').then((contactList: any[]) => {
            setContacts(contactList.map((e: any) => {
                // Patch the ID as a string
                e.id = String(e.id);
                return e;
            }));
            setIsLoading.off();
        }).catch(() => {
            setIsLoading.off();
        });

        if (showToast) {
            showSuccessToast({
                id: 'contacts',
                description: 'Successfully refreshed Contacts!'
            });
        }
    };

    useEffect(() => {
        loadContacts();
        refreshPermissionStatus();
    }, []);

    const getEmptyContent = () => {
        const wrap = (child: JSX.Element) => {
            return (
                <section style={{marginTop: 20}}>
                    {child}
                </section>
            );
        };

        if (isLoading) {
            return wrap(<CircularProgress isIndeterminate />);
        }

        if (contacts.length === 0) {
            return wrap(<Text fontSize="md">BlueBubbles found no contacts in your Mac's Address Book!</Text>);
        }

        return null;
    };

    const filterContacts = () => {
        return filteredContacts.slice((currentPage - 1) * perPage, currentPage * perPage);
    };

    const onCreate = async (contact: ContactItem) => {
        const newContact = await createContact(
            contact.firstName,
            contact.lastName,
            {
                emails: contact.emails.map((e: NodeJS.Dict<any>) => e.address),
                phoneNumbers: contact.phoneNumbers.map((e: NodeJS.Dict<any>) => e.address)
            }
        );

        if (newContact) {
            // Patch the contact using a string ID & source type
            newContact.id = String(newContact.id);
            newContact.sourceType = 'db';

            // Patch the addresses
            (newContact as any).phoneNumbers = (newContact as any).addresses.filter((e: any) => e.type === 'phone');
            (newContact as any).emails = (newContact as any).addresses.filter((e: any) => e.type === 'email');

            setContacts([newContact, ...contacts]);
        }
    };

    const onUpdate = async (contact: NodeJS.Dict<any>) => {
        const cId = typeof(contact.id) === 'string' ? Number.parseInt(contact.id) : contact.id as number;
        const newContact = await updateContact(
            cId,
            {
                firstName: contact.firstName,
                lastName: contact.lastName,
                displayName: contact.displayName
            }
        );

        const copiedContacts = [...contacts];
        let updated = false;
        for (let i = 0; i < copiedContacts.length; i++) {
            if (copiedContacts[i].id === String(cId)) {
                copiedContacts[i].firstName = newContact.firstName;
                copiedContacts[i].lastName = newContact.lastName;
                copiedContacts[i].displayName = newContact.displayName;
                updated = true;
            }
        }

        if (updated) {
            setContacts(copiedContacts);
        }
    };

    const onDelete = async (contactId: number | string) => {
        await deleteContact(typeof(contactId) === 'string' ? Number.parseInt(contactId as string) : contactId);
        setContacts(contacts.filter((e: ContactItem) => {
            return e.id !== String(contactId);
        }));
    };

    const onAddAddress = async (contactId: number | string, address: string) => {
        const cId = typeof(contactId) === 'string' ? Number.parseInt(contactId as string) : contactId;
        const addr = await addAddressToContact(cId, address, address.includes('@') ? 'email' : 'phone');
        if (addr) {
            setContacts(contacts.map((e: ContactItem) => {
                if (e.id !== String(contactId)) return e;
                if (address.includes('@')) {
                    e.emails = [...e.emails, addr];
                } else {
                    e.phoneNumbers = [...e.phoneNumbers, addr];
                }

                return e;
            }));
        }
    };

    const onDeleteAddress = async (contactAddressId: number) => {
        await deleteContactAddress(contactAddressId);
        setContacts(contacts.map((e: ContactItem) => {
            e.emails = e.emails.filter((e: ContactAddress) => e.id !== contactAddressId);
            e.phoneNumbers = e.phoneNumbers.filter((e: ContactAddress) => e.id !== contactAddressId);
            return e;
        }));
    };

    const clearLocalContacts = async () => {
        // Delete the contacts, then filter out the DB items
        await deleteLocalContacts();
        setContacts(contacts.filter(e => e.sourceType !== 'db'));
    };

    const confirmationActions: ConfirmationItems = {
        clearLocalContacts: {
            message: (
                'Are you sure you want to clear/delete all local Contacts?<br /><br />' +
                'This will remove any Contacts added manually, via the API, or via the import process'
            ),
            func: clearLocalContacts
        }
    };

    return (
        <Box p={3} borderRadius={10}>
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Controls</Text>
                <Divider orientation='horizontal' />
                <Box>
                    <Menu>
                        <MenuButton
                            as={Button}
                            rightIcon={<BsChevronDown />}
                            width="12em"mr={5}
                        >
                            Manage
                        </MenuButton>
                        <MenuList>
                            <MenuItem icon={<BsPersonPlus />} onClick={() => setDialogOpen.on()}>
                                Add Contact
                            </MenuItem>
                            <MenuItem icon={<BiRefresh />} onClick={() => loadContacts(true)}>
                                Refresh Contacts
                            </MenuItem>
                            <MenuItem
                                icon={<BiImport />}
                                onClick={() => {
                                    if (inputFile && inputFile.current) {
                                        (inputFile.current as HTMLElement).click();
                                    }
                                }}
                            >
                                Import VCF
                                <input
                                    type='file'
                                    id='file'
                                    ref={inputFile}
                                    accept=".vcf"
                                    style={{display: 'none'}}
                                    onChange={(e) => {
                                        const files = e?.target?.files ?? [];
                                        for (const i of files) {
                                            ipcRenderer.invoke('import-vcf', i.webkitRelativePath);
                                        }
                                    }}
                                />
                            </MenuItem>
                            <MenuDivider />
                            <MenuItem icon={<FiTrash />} onClick={() => confirm('clearLocalContacts')}>
                                Clear Local Contacts
                            </MenuItem>
                        </MenuList>
                    </Menu>
                    <Menu>
                        <MenuButton
                            as={Button}
                            rightIcon={<BsChevronDown />}
                            width="12em"
                            mr={5}
                        >
                            Permissions
                        </MenuButton>
                        <MenuList>
                            <MenuItem icon={<BiRefresh />} onClick={() => refreshPermissionStatus()}>
                                Refresh Permission Status
                            </MenuItem>
                            {(permission !== null && permission !== 'Authorized') ? (
                                <MenuItem icon={<BsUnlockFill />} onClick={() => requestContactPermission()}>
                                    Request Permission
                                </MenuItem>
                            ) : null}
                        </MenuList>
                    </Menu>
                    <Text as="span" verticalAlign="middle">
                        Status: <Text as="span" color={getPermissionColor(permission)}>
                            {permission ? permission : 'Checking...'}
                        </Text>
                    </Text>
                </Box>
            </Stack>
            <Stack direction='column' p={5}>
                <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                    <Text fontSize='2xl'>Contacts ({filteredContacts.length})</Text>
                    <Popover trigger='hover'>
                        <PopoverTrigger>
                            <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                                <AiOutlineInfoCircle />
                            </Box>
                        </PopoverTrigger>
                        <PopoverContent>
                            <PopoverArrow />
                            <PopoverCloseButton />
                            <PopoverHeader>Information</PopoverHeader>
                            <PopoverBody>
                                <Text>
                                    Here are the contacts on your macOS device that BlueBubbles knows about,
                                    and will serve to any clients that want to know about them. These include
                                    contacts from this Mac's Address Book, as well as contacts from uploads/imports
                                    or manual entry.
                                </Text>
                            </PopoverBody>
                        </PopoverContent>
                    </Popover>
                </Flex>
                <Divider orientation='horizontal' />
                <Flex flexDirection='row' justifyContent='flex-end' alignItems='center' pt={3}>
                    <InputGroup width="xxs">
                        <InputLeftElement pointerEvents='none'>
                            <AiOutlineSearch color='gray.300' />
                        </InputLeftElement>
                        <Input
                            placeholder='Search Contacts'
                            onChange={(e) => {
                                if (currentPage > 1) {
                                    setCurrentPage(1);
                                }

                                setSearch(e.target.value);
                            }}
                            value={search}
                        />
                    </InputGroup>
                </Flex>
                <Flex justifyContent="center" alignItems="center">
                    {getEmptyContent()}
                </Flex>
                {(contacts.length > 0) ? (
                    <ContactsTable
                        contacts={filterContacts()}
                        onCreate={onCreate}
                        onDelete={onDelete}
                        onUpdate={onUpdate}
                        onAddressAdd={onAddAddress}
                        onAddressDelete={onDeleteAddress}
                    />
                ) : null}
                <Pagination
                    pagesCount={pagesCount}
                    currentPage={currentPage}
                    onPageChange={setCurrentPage}
                >
                    <PaginationContainer
                        align="center"
                        justify="space-between"
                        w="full"
                        pt={2}
                    >
                        <PaginationPrevious minWidth={'75px'}>Previous</PaginationPrevious>
                        <Box ml={1}></Box>
                        <PaginationPageGroup flexWrap="wrap" justifyContent="center">
                            {pages.map((page: number) => (
                                <PaginationPage 
                                    key={`pagination_page_${page}`} 
                                    page={page}
                                    my={1}
                                    px={3}
                                    fontSize={14}
                                />
                            ))}
                        </PaginationPageGroup>
                        <Box ml={1}></Box>
                        <PaginationNext minWidth={'50px'}>Next</PaginationNext>
                    </PaginationContainer>
                </Pagination>
            </Stack>

            <ContactDialog
                modalRef={dialogRef}
                isOpen={dialogOpen}
                onCreate={onCreate}
                onDelete={onDelete}
                onAddressAdd={onAddAddress}
                onAddressDelete={onDeleteAddress}
                onClose={() => setDialogOpen.off()}
            />

            <ConfirmationDialog
                modalRef={alertRef}
                onClose={() => confirm(null)}
                body={confirmationActions[requiresConfirmation as string]?.message}
                onAccept={() => {
                    confirmationActions[requiresConfirmation as string].func();
                }}
                isOpen={requiresConfirmation !== null}
            />
        </Box>
    );
}
Example #4
Source File: youtubeWrapper.tsx    From podcast-maker with MIT License 4 votes vote down vote up
YoutubeWrapper: React.FC<WrapperProps> = ({children, title}) => {
	const {
		width: videoWidth,
		height: videoHeight,
		durationInFrames,
	} = useVideoConfig();
	const frame = useCurrentFrame();

	const startScaleAnimationAtFrame = 30;
	const startClickAnimationAtFrame = 50 + startScaleAnimationAtFrame;

	//Mouse movement
	const moveY = interpolate(
		frame,
		[0, 50, 57, 73, 90, 110, 115, 125, 150].map(
			(value) => value + startClickAnimationAtFrame
		),
		[-50, 95, 93, 97, 20, 10, 15, 12, -50]
	);
	const moveX = interpolate(
		frame,
		[0, 50, 57, 73, 90, 110, 115, 125, 150].map(
			(value) => value + startClickAnimationAtFrame
		),
		[370, 650, 657, 655, 40, 50, 45, 48, 150]
	);
	const colorsClick = interpolateColors(
		frame,
		[0, 50, 57, 73, 90, 110, 115, 125, 150].map(
			(value) => value + startClickAnimationAtFrame
		),
		['#fff', '#fff', '#aaa', '#fff', '#fff', '#fff', '#aaa', '#fff', '#fff']
	);

	//Scale change
	const scale = interpolate(
		frame,
		[
			0,
			0 + startScaleAnimationAtFrame,
			startClickAnimationAtFrame,
			200 + startScaleAnimationAtFrame,
			250 + startScaleAnimationAtFrame,
			durationInFrames,
		],
		[1.57, 1.57, 1, 1, 1.57, 1.57]
	);
	const translateX = interpolate(
		frame,
		[
			0,
			0 + startScaleAnimationAtFrame,
			startClickAnimationAtFrame,
			200 + startScaleAnimationAtFrame,
			250 + startScaleAnimationAtFrame,
			durationInFrames,
		],
		[16.3, 16.3, 0, 0, 16.3, 16.3]
	);
	const translateY = interpolate(
		frame,
		[
			0,
			0 + startScaleAnimationAtFrame,
			startClickAnimationAtFrame,
			200 + startScaleAnimationAtFrame,
			250 + startScaleAnimationAtFrame,
			durationInFrames,
		],
		[7.3, 7.3, 0, 0, 7.3, 7.3]
	);

	return (
		<div
			style={{
				transform: `scale(${scale}) translate(${translateX}%, ${translateY}%)`,
				background: '#212121',
			}}
		>
			<Sequence
				from={47 + startClickAnimationAtFrame}
				durationInFrames={Infinity}
			>
				<Audio src={clickSound} startFrom={30} />
			</Sequence>
			<Sequence
				from={105 + startClickAnimationAtFrame}
				durationInFrames={Infinity}
			>
				<Audio src={clickSound} startFrom={30} />
			</Sequence>
			<header
				style={{
					background: '#202020',
					padding: '1rem',
					display: 'flex',
					justifyContent: 'space-between',
					alignItems: 'center',
					width: videoWidth,
				}}
			>
				<div
					style={{display: 'flex', alignItems: 'center', gap: '2rem'}}
				>
					<AiOutlineMenu size="3rem" color="#fff" />
					<img src={ytLogo} />
				</div>

				<div style={{display: 'flex', alignItems: 'center', gap: '0'}}>
					<div
						style={{
							height: '3rem',
							width: 700,
							background: '#121212',
							border: '1px solid #323232',
							borderRadius: '3px 0 0 3px',
						}}
					></div>
					<div
						style={{
							height: '3rem',
							width: 130,
							background: '#323232',
							border: '1px solid #323232',
							borderRadius: '0 3px 3px 0',
							display: 'flex',
							justifyContent: 'center',
							alignItems: 'center',
						}}
					>
						<AiOutlineSearch color="#999" size="1.8rem" />
					</div>
					<IoMdMic
						style={{marginLeft: '1rem'}}
						size="2rem"
						color="#fff"
					/>
				</div>

				<div
					style={{display: 'flex', alignItems: 'center', gap: '2rem'}}
				>
					<img
						src={avatar}
						style={{
							width: '2.75rem',
							height: '2.75rem',
							borderRadius: '2rem',
							marginRight: '2rem',
						}}
					/>
				</div>
			</header>
			<div style={{display: 'flex', flexDirection: 'row'}}>
				<main>
					<VideoWrapper
						videoWidth={videoWidth}
						videoHeight={videoHeight}
					>
						{children}
					</VideoWrapper>
					<Footer videoWidth={videoWidth}>
						<h1>[CodeStack News] {title}</h1>
						<div>
							<p>
								<span>
									{Math.round(frame / 3)} Visualizações
								</span>
								<span>
									{format(
										new Date(),
										"dd 'de' MMM. 'de' yyyy",
										{
											locale: ptBR,
										}
									)}
								</span>
							</p>
							<div>
								<section
									style={{
										borderBottom: `3px solid ${
											frame >=
											57 + startClickAnimationAtFrame
												? '#3EA6FF'
												: ''
										}`,
									}}
								>
									<text
										style={{
											color:
												frame >=
												57 + startClickAnimationAtFrame
													? '#3EA6FF'
													: '',
										}}
									>
										<IoMdThumbsUp size="3rem" />
										{Math.round(frame / 10)}
									</text>
									<text>
										<IoMdThumbsDown size="3rem" /> 0
									</text>
								</section>
								<text>
									<IoIosShareAlt size="3rem" />
									Compartilhar
								</text>
								<text>
									<RiPlayListAddLine size="2rem" />
									Salvar
								</text>
								<BsThreeDots size="3rem" />
							</div>
						</div>
						<div style={{margin: '0.5rem 0'}}>
							<div>
								<img
									src={avatar}
									style={{
										width: '4.5rem',
										height: '4.5rem',
										borderRadius: '2.5rem',
										marginRight: '0rem',
									}}
								/>
								<p
									style={{
										display: 'flex',
										flexDirection: 'column',
									}}
								>
									<strong
										style={{
											fontSize: '1.5rem',
											color: '#fff',
										}}
									>
										CodeStack
									</strong>
									<span>
										{Math.round(frame / 15)} inscritos
									</span>
								</p>
							</div>
							<p
								style={{
									background:
										frame >=
										115 + startClickAnimationAtFrame
											? '#303030'
											: '#CC0000',
									color: '#fff',
									fontSize: '1.7rem',
									textTransform: 'uppercase',
									padding: '1rem 2rem',
									margin: 0,
									borderRadius: 5,
								}}
							>
								{frame >= 115 + startClickAnimationAtFrame
									? 'Inscrito'
									: 'Inscreva-se'}
							</p>
						</div>
						<div
							style={{
								position: 'absolute',
								bottom: moveY, //-50 -> 95 -> 10 -> -50
								right: moveX, //370 -> 670 -> 50 -> 150
							}}
						>
							<svg
								width="3rem"
								height="3rem"
								viewBox="0 0 23 25"
								fill="none"
								xmlns="http://www.w3.org/2000/svg"
							>
								<g clipPath="url(#prefix__clip0)">
									<path
										d="M22.326 11.773v4.5c0 .145-.018.29-.053.43l-1.571 6.375c-.209.847-1 1.445-1.912 1.445H8.576c-.31 0-.616-.07-.892-.204a1.944 1.944 0 01-.697-.568l-6.285-8.25c-.639-.837-.445-2.01.433-2.619.877-.609 2.106-.424 2.744.414l1.554 2.04V2.398c0-1.035.88-1.875 1.964-1.875 1.085 0 1.964.84 1.964 1.875v9.375h.393V9.898c0-1.035.88-1.875 1.965-1.875 1.084 0 1.964.84 1.964 1.875v1.875h.393v-1.125c0-1.035.88-1.875 1.964-1.875 1.085 0 1.964.84 1.964 1.875v1.125h.393c0-1.035.88-1.875 1.964-1.875 1.085 0 1.965.84 1.965 1.875zm-12.572 3.75h-.393v4.5h.393v-4.5zm4.322 0h-.393v4.5h.393v-4.5zm4.321 0h-.393v4.5h.393v-4.5z"
										fill={colorsClick}
									/>
								</g>
							</svg>
						</div>
					</Footer>
				</main>
				<Aside>
					{new Array(7).fill(0).map((_, i) => {
						return (
							<div key={i}>
								<main></main>
								<p>
									<strong></strong>
									<span></span>
								</p>
							</div>
						);
					})}
				</Aside>
			</div>
		</div>
	);
}
Example #5
Source File: SearchForm.tsx    From game-store-monorepo-app with MIT License 4 votes vote down vote up
SearchForm: React.FC<SearchFormProps> = ({ className, ...rest }) => {
  const navigate = useNavigate();
  const [searchTerm, setSearchTerm] = React.useState('');
  const [searchVisible, setSearchVisible] = React.useState(false);
  const debouncedSearchTerm = useDebounce(searchTerm);
  const [searchGames, { data, loading }] = useLazyQuery<SearchGamesQueryResponse>(SEARCH_GAMES);

  const listClassName = cn({
    hidden: !searchVisible,
  });

  React.useEffect(() => {
    if (debouncedSearchTerm) {
      setSearchVisible(true);
    } else {
      setSearchVisible(false);
    }
  }, [debouncedSearchTerm]);

  React.useEffect(() => {
    if (!debouncedSearchTerm) {
      return;
    }

    const queryParams: GamesQueryParams = {
      variables: {
        page: 1,
        pageSize: 10,
        search: debouncedSearchTerm,
      },
    };
    searchGames(queryParams);
  }, [debouncedSearchTerm, searchGames, searchVisible]);

  const gameResults = data?.searchGames?.results;

  const listData: ListItem[] = React.useMemo(() => {
    if (!gameResults) {
      return [];
    }
    return gameResults.map((item): ListItem => {
      return {
        id: item.id,
        avatarUrl: item.thumbnailImage,
        title: item.name,
        content: (
          <div>
            <PlatformLogos data={item.parentPlatforms} className="mt-1" />
            <p className="mt-2 text-sm text-base-content-secondary truncate">{`${getMultipleItemNames(
              item.genres,
              3,
            )}`}</p>
          </div>
        ),
      };
    });
  }, [gameResults]);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSearchTerm(e.target.value);
  };

  const handleBlur = () => {
    setTimeout(() => {
      setSearchVisible(false);
    }, 200);
  };

  const handleFocus = () => {
    if (!gameResults?.length) {
      return;
    }
    setSearchVisible(true);
  };

  const onItemClick = (value: ListItem) => {
    navigate(`${ROUTES.GAMES}/${value.id}`);
  };

  const onSearchButtonClick = () => {
    navigate(`${ROUTES.GAMES}?search=${searchTerm}`);
  };

  return (
    <div className={cn(className)} {...rest}>
      <FormInput
        value={searchTerm}
        onChange={handleChange}
        placeholder="Search games..."
        addonElement={
          <Button variant="primary" className="font-bold btn-addon-right" onClick={onSearchButtonClick}>
            <AiOutlineSearch size={16} />
          </Button>
        }
        onBlur={handleBlur}
        onFocus={handleFocus}
      />
      <List
        data={listData}
        onItemClick={onItemClick}
        isLoading={loading}
        className={cn(
          'absolute w-full max-h-96 min-h-12 top-[55px] bg-base-100 overflow-y-auto rounded-xl shadow-2xl',
          listClassName,
        )}
      />
    </div>
  );
}