Create AccountSearch component to do more than AutosuggestAccountInput

This commit is contained in:
Alex Gleason 2021-10-14 11:47:10 -05:00
parent b0b13e9e59
commit a02112a1c8
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
4 changed files with 112 additions and 18 deletions

View file

@ -0,0 +1,83 @@
import React from 'react';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import Icon from 'soapbox/components/icon';
import AutosuggestAccountInput from 'soapbox/components/autosuggest_account_input';
import classNames from 'classnames';
const messages = defineMessages({
placeholder: { id: 'account_search.placeholder', defaultMessage: 'Search for an account' },
});
export default @injectIntl
class AccountSearch extends React.PureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
onSelected: PropTypes.func.isRequired,
};
state = {
value: '',
}
isEmpty = () => {
const { value } = this.state;
return !(value.length > 0);
}
clearState = () => {
this.setState({ value: '' });
}
handleChange = ({ target }) => {
this.setState({ value: target.value });
}
handleSelected = accountId => {
this.clearState();
this.props.onSelected(accountId);
}
handleClear = e => {
e.preventDefault();
if (!this.isEmpty()) {
this.setState({ value: '' });
}
}
handleKeyDown = e => {
if (e.key === 'Escape') {
document.querySelector('.ui').parentElement.focus();
}
}
render() {
const { intl, onSelected, ...rest } = this.props;
const { value } = this.state;
const isEmpty = this.isEmpty();
return (
<div className='search search--account'>
<label>
<span style={{ display: 'none' }}>{intl.formatMessage(messages.placeholder)}</span>
<AutosuggestAccountInput
className='search__input'
placeholder={intl.formatMessage(messages.placeholder)}
value={value}
onChange={this.handleChange}
onSelected={this.handleSelected}
onKeyDown={this.handleKeyDown}
{...rest}
/>
</label>
<div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
<Icon src={require('@tabler/icons/icons/search.svg')} className={classNames('svg-icon--search', { active: isEmpty })} />
<Icon src={require('@tabler/icons/icons/backspace.svg')} className={classNames('svg-icon--backspace', { active: !isEmpty })} aria-label={intl.formatMessage(messages.placeholder)} />
</div>
</div>
);
}
}

View file

@ -4,34 +4,29 @@ import PropTypes from 'prop-types';
import { CancelToken } from 'axios'; import { CancelToken } from 'axios';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { injectIntl, defineMessages } from 'react-intl';
import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import { OrderedSet as ImmutableOrderedSet } from 'immutable';
import { accountSearch } from 'soapbox/actions/accounts'; import { accountSearch } from 'soapbox/actions/accounts';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
const noOp = () => {}; const noOp = () => {};
const messages = defineMessages({
placeholder: { id: 'autosuggest_account_input.default_placeholder', defaultMessage: 'Search for an account' },
});
export default @connect() export default @connect()
@injectIntl
class AutosuggestAccountInput extends ImmutablePureComponent { class AutosuggestAccountInput extends ImmutablePureComponent {
static propTypes = { static propTypes = {
intl: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onSelected: PropTypes.func.isRequired, onSelected: PropTypes.func.isRequired,
limit: PropTypes.number, value: PropTypes.string.isRequired,
limit: PropTypes.number.isRequired,
} }
static defaultProps = { static defaultProps = {
value: '',
limit: 4, limit: 4,
} }
state = { state = {
text: '',
accountIds: ImmutableOrderedSet(), accountIds: ImmutableOrderedSet(),
} }
@ -43,6 +38,10 @@ class AutosuggestAccountInput extends ImmutablePureComponent {
return this.source; return this.source;
} }
clearResults = () => {
this.setState({ accountIds: ImmutableOrderedSet() });
}
handleAccountSearch = throttle(q => { handleAccountSearch = throttle(q => {
const { dispatch, limit } = this.props; const { dispatch, limit } = this.props;
const source = this.refreshCancelToken(); const source = this.refreshCancelToken();
@ -58,23 +57,28 @@ class AutosuggestAccountInput extends ImmutablePureComponent {
}, 900, { leading: true, trailing: true }) }, 900, { leading: true, trailing: true })
handleChange = ({ target }) => { handleChange = e => {
this.handleAccountSearch(target.value); this.handleAccountSearch(e.target.value);
this.setState({ text: target.value }); this.props.onChange(e);
} }
handleSelected = (tokenStart, lastToken, accountId) => { handleSelected = (tokenStart, lastToken, accountId) => {
this.props.onSelected(accountId); this.props.onSelected(accountId);
} }
componentDidUpdate(prevProps) {
if (this.props.value === '' && prevProps.value !== '') {
this.clearResults();
}
}
render() { render() {
const { intl, ...rest } = this.props; const { intl, value, onChange, ...rest } = this.props;
const { text, accountIds } = this.state; const { accountIds } = this.state;
return ( return (
<AutosuggestInput <AutosuggestInput
placeholder={intl.formatMessage(messages.placeholder)} value={value}
value={text}
onChange={this.handleChange} onChange={this.handleChange}
suggestions={accountIds.toList()} suggestions={accountIds.toList()}
onSuggestionsFetchRequested={noOp} onSuggestionsFetchRequested={noOp}

View file

@ -8,10 +8,11 @@ import { expandDirectTimeline } from '../../actions/timelines';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connectDirectStream } from '../../actions/streaming'; import { connectDirectStream } from '../../actions/streaming';
import { directComposeById } from 'soapbox/actions/compose'; import { directComposeById } from 'soapbox/actions/compose';
import AutosuggestAccountInput from 'soapbox/components/autosuggest_account_input'; import AccountSearch from 'soapbox/components/account_search';
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'column.direct', defaultMessage: 'Direct messages' }, title: { id: 'column.direct', defaultMessage: 'Direct messages' },
searchPlaceholder: { id: 'direct.search_placeholder', defaultMessage: 'Search for an account to message…' },
}); });
const mapStateToProps = state => ({ const mapStateToProps = state => ({
@ -62,7 +63,10 @@ class DirectTimeline extends React.PureComponent {
onPin={this.handlePin} onPin={this.handlePin}
/> />
<AutosuggestAccountInput onSelected={this.handleSuggestion} /> <AccountSearch
placeholder={intl.formatMessage(messages.searchPlaceholder)}
onSelected={this.handleSuggestion}
/>
<StatusListContainer <StatusListContainer
scrollKey='direct_timeline' scrollKey='direct_timeline'

View file

@ -167,9 +167,12 @@
.search-page { .search-page {
height: 100%; height: 100%;
}
.column {
.search { .search {
padding: 10px 15px; padding: 10px 15px;
background-color: var(--foreground-color);
border-bottom: 1px solid hsla(var(--primary-text-color_hsl), 0.2); border-bottom: 1px solid hsla(var(--primary-text-color_hsl), 0.2);
} }