diff --git a/src/pages/homepage/components/Search/Search.tsx b/src/pages/homepage/components/Search/Search.tsx index bce365d..2a7e7f7 100644 --- a/src/pages/homepage/components/Search/Search.tsx +++ b/src/pages/homepage/components/Search/Search.tsx @@ -1,9 +1,17 @@ -import { Group, type OptionsFilter, Select, Stack, Text } from '@mantine/core'; +import { + ActionIcon, + Group, + type OptionsFilter, + Select, + Stack, + Text, +} from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; import { HelpModal, WeatherModal } from 'components'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useEffect, useState } from 'react'; -import { selectClasses } from 'styles'; +import { FiEdit, FiTrash } from 'react-icons/fi'; +import { actionIconClasses, selectClasses } from 'styles'; export default function Search({ current, @@ -25,10 +33,13 @@ export default function Search({ endpoint: 'api/searchHistory', }); const [searchHistory, setSearchHistory] = useState([]); + const [filteredHistory, setFilteredHistory] = useState([]); const { mutateAsync: createHistoryItem } = useMutationAPI(); + const { mutateAsync: deleteHistoryItem } = useMutationAPI(); const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] = useDisclosure(false); const [dropdownOpened, { open: openDropdown, close: closeDropdown }] = useDisclosure(); + const [listIndex, setListIndex] = useState(0); const { theme, providerLabel, @@ -40,7 +51,8 @@ export default function Search({ searchValue, setSearchValue, } = useCommonHooks(); - + const [placeholderValue, setPlaceholderValue] = useState(''); + const [value, setValue] = useState(0); const urlPattern = new RegExp( '^(https?:\\/\\/)?' + // protocol '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name @@ -107,6 +119,117 @@ export default function Search({ } }; + const handleKeyUp = (item: any) => { + if (item.code === 'Escape') { + if (dropdownOpened) { + closeDropdown(); + } + setListIndex(0); + setSearchValue(''); + setProviderLabel('Google Search'); + setProviderPrefix('/g'); + setPlaceholderValue(''); + } + if (item.code === 'ArrowDown' && !dropdownOpened) { + setListIndex(0); + openDropdown(); + setPlaceholderValue( + `${searchHistory[listIndex].prefix} ${searchHistory[listIndex].searchTerm}`, + ); + } + if (!providerLabel.startsWith('Invalid')) { + handleKeyPress(item); + } + if (dropdownOpened) { + if (searchValue === '') { + if (item.code === 'ArrowUp') { + if (listIndex === 0) { + setListIndex(9); + } else { + setListIndex(listIndex - 1); + } + } else if (item.code === 'ArrowDown') { + if (listIndex === 9) { + setListIndex(0); + } else { + setListIndex(listIndex + 1); + } + } else if (item.code === 'ArrowRight' || item.code === 'Tab') { + setSearchValue( + `${searchHistory[listIndex].prefix} ${searchHistory[listIndex].searchTerm}`, + ); + setValue(searchHistory[listIndex].id); + } + } else if (searchValue !== '') { + if (item.code === 'ArrowUp') { + if (listIndex === 0) { + setListIndex(filteredHistory.length - 1); + } else if (listIndex >= filteredHistory.length) { + setListIndex(filteredHistory.length - 1); + } else { + setListIndex(listIndex - 1); + } + } else if (item.code === 'ArrowDown') { + if (listIndex >= filteredHistory.length - 1) { + setListIndex(0); + } else if (value === 0) { + setListIndex(0); + setValue(-1); + } else { + setListIndex(listIndex + 1); + } + } else if (item.code === 'ArrowRight' || item.code === 'Tab') { + setSearchValue( + `${filteredHistory[listIndex].prefix} ${filteredHistory[listIndex].searchTerm}`, + ); + setValue(filteredHistory[listIndex].id); + } + } + } + }; + + const onInput = (item: any) => { + openDropdown(); + setSearchValue(item.currentTarget.value); + if ( + item.target.value === '/?' || + item.target.value === '!help' || + item.target.value === '/help' || + item.target.value === '!h' || + item.target.value === '/h' + ) { + setProviderLabel('Show help'); + } else if (item.target.value.startsWith('/g')) { + setProviderLabel('Google Search'); + setProviderURL('https://www.google.com/search?q='); + setProviderPrefix('/g'); + } else if (item.target.value.startsWith('/')) { + const provider = providerData?.find( + (p: any) => p.prefix === item.target.value.split(' ')[0], + ); + setProviderLabel(provider?.name || 'Invalid Search'); + setProviderURL(provider?.url); + setProviderPrefix(provider?.prefix); + } else if (item.target.value.startsWith('!')) { + if (item.target.value === '!w' || item.target.value === '!weather') { + setProviderLabel('Show weather'); + } else if ( + applicationData?.find( + (app: any) => + app.baseUrl.substr(1).toLowerCase() === item.target.value.substr(1), + ) + ) { + setProviderLabel(`Launch ${item.target.value.substr(1)}`); + } else { + setProviderLabel('Invalid function'); + } + } else { + setProviderLabel('Google Search'); + setProviderURL('https://www.google.com/search?q='); + setProviderPrefix('/g'); + } + }; + const optionsFilter: OptionsFilter = ({ options, search }) => { const splittedSearch = search.toLowerCase().trim().split(' '); return options @@ -119,6 +242,77 @@ export default function Search({ .slice(0, 10); }; + function RenderOption(item: any) { + const [showFillButton, setShowFillButton] = useState(false); + const m = new Date(item.option.dateTimeStamp); + const dateString = `${m.getUTCFullYear()}/${`0${m.getUTCMonth() + 1}`.slice(-2)}/${`0${m.getUTCDate()}`.slice(-2)} ${`0${m.getUTCHours()}`.slice(-2)}:${`0${m.getUTCMinutes()}`.slice(-2)}:${`0${m.getUTCSeconds()}`.slice(-2)}`; + return ( + { + handleSearchHistoryItem(item); + }} + onMouseEnter={() => { + setShowFillButton(true); + }} + onMouseLeave={() => { + setShowFillButton(false); + }} + > + + + {item.option.provider} + + {showFillButton && ( + { + e.stopPropagation(); + setSearchValue(item.option.searchTerm); + }} + > + + + )} + + + {item.option.searchTerm} + + {dateString} + + + {showFillButton && ( + { + e.stopPropagation(); + deleteHistoryItem({ + method: 'DELETE', + endpoint: 'api/searchHistory', + body: { id: item.option.id }, + }); + }} + > + + + )} + + ); + } + useEffect(() => { if (providers && providersIsFetched) { setProviderData(providers); @@ -134,8 +328,33 @@ export default function Search({ useEffect(() => { if (history && historyIsFetched) { setSearchHistory(history); + if (filteredHistory.length === 0) { + setFilteredHistory(history.slice(0, 10)); + } } }, [history, historyIsFetched]); + + useEffect(() => { + if (dropdownOpened) { + setPlaceholderValue( + `${searchHistory[listIndex].prefix} ${searchHistory[listIndex].searchTerm}`, + ); + } + }, [listIndex, dropdownOpened]); + + useEffect(() => { + const splittedSearch = searchValue.toLowerCase().trim().split(' '); + const filtered = searchHistory + .filter((item: any) => { + const option = `${item.prefix} ${item.searchTerm}`; + const words = option.toLowerCase().trim().split(' '); + return splittedSearch.every((searchWord) => + words.some((word: any) => word.includes(searchWord)), + ); + }) + .slice(0, 10); + setFilteredHistory(filtered); + }, [searchValue]); return ( ({ label: `${item.provider}`, value: `${item.prefix} ${item.searchTerm} ${item.dateTimeStamp}`, ...item, }))} - renderOption={(item: any) => { - const m = new Date(item.option.dateTimeStamp); - const dateString = `${m.getUTCFullYear()}/${`0${m.getUTCMonth() + 1}`.slice(-2)}/${`0${m.getUTCDate()}`.slice(-2)} ${`0${m.getUTCHours()}`.slice(-2)}:${`0${m.getUTCMinutes()}`.slice(-2)}:${`0${m.getUTCSeconds()}`.slice(-2)}`; - return ( - { - handleSearchHistoryItem(item); - }} - > - - - {item.option.label} - - - - {item.option.searchTerm} - - {dateString} - - - - ); - }} - spellCheck='true' - leftSection={providerLabel} - rightSection={} + filter={optionsFilter} value={searchValue} searchValue={searchValue} - autoFocus - placeholder={searchValue === '' ? undefined : searchValue} - nothingFoundMessage='Nothing found...' + placeholder={placeholderValue} + nothingFoundMessage={Nothing found...} data-active={searchValue !== ''} dropdownOpened={dropdownOpened} - onBlur={() => closeDropdown()} + comboboxProps={{ + withinPortal: false, + transitionProps: { transition: 'fade', duration: 200 }, + }} + spellCheck='true' + searchable + autoFocus + leftSection={providerLabel} + rightSection={} + renderOption={RenderOption} + onBlur={() => { + setPlaceholderValue(''); + setListIndex(0); + closeDropdown(); + }} onFocus={() => { if (searchValue !== '') { openDropdown(); } }} - comboboxProps={{ - withinPortal: false, - transitionProps: { transition: 'fade', duration: 200 }, - }} - onInput={(item: any) => { - openDropdown(); - setSearchValue(item.currentTarget.value); - if ( - item.target.value === '/?' || - item.target.value === '!help' || - item.target.value === '/help' || - item.target.value === '!h' || - item.target.value === '/h' - ) { - setProviderLabel('Show help'); - } else if (item.target.value.startsWith('/g')) { - setProviderLabel('Google Search'); - setProviderURL('https://www.google.com/search?q='); - setProviderPrefix('/g'); - } else if (item.target.value.startsWith('/')) { - const provider = providerData?.find( - (p: any) => p.prefix === item.target.value.split(' ')[0], - ); - setProviderLabel(provider?.name || 'Invalid Search'); - setProviderURL(provider?.url); - setProviderPrefix(provider?.prefix); - } else if (item.target.value.startsWith('!')) { - if (item.target.value === '!w' || item.target.value === '!weather') { - setProviderLabel('Show weather'); - } else if ( - applicationData?.find( - (app: any) => - app.baseUrl.substr(1).toLowerCase() === item.target.value.substr(1), - ) - ) { - setProviderLabel(`Launch ${item.target.value.substr(1)}`); - } else { - setProviderLabel('Invalid function'); - } - } else { - setProviderLabel('Google Search'); - setProviderURL('https://www.google.com/search?q='); - setProviderPrefix('/g'); - } - }} - onKeyUp={(item: any) => { - if (item.code === 'Escape') { - if (dropdownOpened) { - closeDropdown(); - } - setSearchValue(''); - setProviderLabel('Google Search'); - setProviderPrefix('/g'); - } - if (item.code === 'ArrowDown' && !dropdownOpened) { - openDropdown(); - } - if (!providerLabel.startsWith('Invalid')) { - handleKeyPress(item); + onKeyDown={(e) => { + if (e.key === 'Tab') { + e.preventDefault(); } }} + onInput={onInput} + onKeyUp={handleKeyUp} />