bigbuffet-rw/app/soapbox/features/compose/components/search.tsx

173 lines
4.5 KiB
TypeScript
Raw Normal View History

import classNames from 'clsx';
2022-03-21 11:09:01 -07:00
import { Map as ImmutableMap } from 'immutable';
import debounce from 'lodash/debounce';
import React, { useCallback } from 'react';
2022-03-21 11:09:01 -07:00
import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
2022-03-22 05:42:26 -07:00
import { useHistory } from 'react-router-dom';
2022-03-21 11:09:01 -07:00
import {
changeSearch,
clearSearch,
clearSearchResults,
setSearchAccount,
2022-03-21 11:09:01 -07:00
showSearch,
submitSearch,
} from 'soapbox/actions/search';
2022-11-15 06:10:14 -08:00
import AutosuggestAccountInput from 'soapbox/components/autosuggest-account-input';
import { Input } from 'soapbox/components/ui';
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
import { useAppSelector } from 'soapbox/hooks';
2022-03-21 11:09:01 -07:00
const messages = defineMessages({
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
action: { id: 'search.action', defaultMessage: 'Search for “{query}”' },
});
function redirectToAccount(accountId: string, routerHistory: any) {
2022-03-21 11:09:01 -07:00
return (_dispatch: any, getState: () => ImmutableMap<string, any>) => {
const acct = getState().getIn(['accounts', accountId, 'acct']);
if (acct && routerHistory) {
routerHistory.push(`/@${acct}`);
}
};
}
2022-03-22 05:42:26 -07:00
interface ISearch {
2022-03-21 11:09:01 -07:00
autoFocus?: boolean,
autoSubmit?: boolean,
autosuggest?: boolean,
openInRoute?: boolean
}
const Search = (props: ISearch) => {
const {
autoFocus = false,
autoSubmit = false,
autosuggest = false,
openInRoute = false,
} = props;
const dispatch = useDispatch();
2022-03-22 05:42:26 -07:00
const history = useHistory();
2022-03-21 11:09:01 -07:00
const intl = useIntl();
const value = useAppSelector((state) => state.search.value);
const submitted = useAppSelector((state) => state.search.submitted);
2022-03-21 11:09:01 -07:00
const debouncedSubmit = useCallback(debounce(() => {
2022-03-21 11:09:01 -07:00
dispatch(submitSearch());
}, 900), []);
2022-03-21 11:09:01 -07:00
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
dispatch(changeSearch(value));
if (autoSubmit) {
debouncedSubmit();
}
};
const handleClear = (event: React.MouseEvent<HTMLDivElement>) => {
event.preventDefault();
if (value.length > 0 || submitted) {
dispatch(clearSearchResults());
2022-03-21 11:09:01 -07:00
}
};
const handleSubmit = () => {
if (openInRoute) {
dispatch(setSearchAccount(null));
dispatch(submitSearch());
2022-03-21 11:09:01 -07:00
history.push('/search');
} else {
dispatch(submitSearch());
2022-03-21 11:09:01 -07:00
}
};
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
event.preventDefault();
handleSubmit();
} else if (event.key === 'Escape') {
document.querySelector('.ui')?.parentElement?.focus();
2022-03-21 11:09:01 -07:00
}
};
const handleFocus = () => {
dispatch(showSearch());
};
const handleSelected = (accountId: string) => {
2022-03-21 11:09:01 -07:00
dispatch(clearSearch());
dispatch(redirectToAccount(accountId, history));
};
const makeMenu = () => [
{
text: intl.formatMessage(messages.action, { query: value }),
icon: require('@tabler/icons/search.svg'),
2022-03-21 11:09:01 -07:00
action: handleSubmit,
},
];
const hasValue = value.length > 0 || submitted;
const componentProps: any = {
type: 'text',
id: 'search',
placeholder: intl.formatMessage(messages.placeholder),
value,
onChange: handleChange,
onKeyDown: handleKeyDown,
onFocus: handleFocus,
autoFocus: autoFocus,
theme: 'search',
className: 'pr-10 rtl:pl-10 rtl:pr-3',
};
if (autosuggest) {
componentProps.onSelected = handleSelected;
componentProps.menu = makeMenu();
componentProps.autoSelect = false;
}
2022-03-21 11:09:01 -07:00
return (
<div className='w-full'>
<label htmlFor='search' className='sr-only'>{intl.formatMessage(messages.placeholder)}</label>
<div className='relative'>
{autosuggest ? (
<AutosuggestAccountInput {...componentProps} />
) : (
<Input {...componentProps} />
)}
2022-03-21 11:09:01 -07:00
<div
role='button'
tabIndex={0}
className='absolute inset-y-0 right-0 rtl:left-0 rtl:right-auto px-3 flex items-center cursor-pointer'
2022-03-21 11:09:01 -07:00
onClick={handleClear}
>
<SvgIcon
src={require('@tabler/icons/search.svg')}
className={classNames('h-4 w-4 text-gray-600', { hidden: hasValue })}
2022-03-21 11:09:01 -07:00
/>
<SvgIcon
src={require('@tabler/icons/x.svg')}
className={classNames('h-4 w-4 text-gray-600', { hidden: !hasValue })}
2022-03-21 11:09:01 -07:00
aria-label={intl.formatMessage(messages.placeholder)}
/>
</div>
</div>
</div>
);
};
2022-03-22 05:42:26 -07:00
export default Search;