Create AccountSearch component to do more than AutosuggestAccountInput
This commit is contained in:
parent
b0b13e9e59
commit
a02112a1c8
4 changed files with 112 additions and 18 deletions
83
app/soapbox/components/account_search.js
Normal file
83
app/soapbox/components/account_search.js
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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}
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue