Added search bar to tags list

This commit is contained in:
Alejandro Celaya 2018-08-19 20:52:33 +02:00
parent 843c121285
commit 96adb227d9
3 changed files with 48 additions and 10 deletions

View file

@ -1,11 +1,12 @@
import React from 'react';
import { connect } from 'react-redux';
import { pick, splitEvery } from 'ramda';
import { listTags } from './reducers/tagsList';
import { filterTags, listTags } from './reducers/tagsList';
import MuttedMessage from '../utils/MuttedMessage';
import TagCard from './TagCard';
import SearchField from '../utils/SearchField';
const { round } = Math;
const { ceil } = Math;
export class TagsList extends React.Component {
state = { isDeleteModalOpen: false };
@ -29,12 +30,12 @@ export class TagsList extends React.Component {
);
}
const tagsCount = tagsList.tags.length;
const tagsCount = tagsList.filteredTags.length;
if (tagsCount < 1) {
return <MuttedMessage>No tags found</MuttedMessage>;
}
const tagsGroups = splitEvery(round(tagsCount / 4), tagsList.tags);
const tagsGroups = splitEvery(ceil(tagsCount / 4), tagsList.filteredTags);
return (
<React.Fragment>
@ -54,8 +55,17 @@ export class TagsList extends React.Component {
}
render() {
const { filterTags } = this.props;
return (
<div className="shlink-container">
{!this.props.tagsList.loading && (
<SearchField
onChange={filterTags}
className="mb-3"
placeholder="Search tags..."
/>
)}
<div className="row">
{this.renderContent()}
</div>
@ -64,4 +74,4 @@ export class TagsList extends React.Component {
}
}
export default connect(pick(['tagsList']), { listTags })(TagsList);
export default connect(pick(['tagsList']), { listTags, filterTags })(TagsList);

View file

@ -6,9 +6,11 @@ import { TAG_EDITED } from './tagEdit';
const LIST_TAGS_START = 'shlink/tagsList/LIST_TAGS_START';
const LIST_TAGS_ERROR = 'shlink/tagsList/LIST_TAGS_ERROR';
const LIST_TAGS = 'shlink/tagsList/LIST_TAGS';
const FILTER_TAGS = 'shlink/tagsList/FILTER_TAGS';
const defaultState = {
tags: [],
filteredTags: [],
loading: false,
error: false,
};
@ -30,20 +32,31 @@ export default function reducer(state = defaultState, action) {
case LIST_TAGS:
return {
tags: action.tags,
filteredTags: action.tags,
loading: false,
error: false,
};
case TAG_DELETED:
return {
...state,
// FIXME This should be optimized somehow...
tags: reject(tag => tag === action.tag, state.tags),
filteredTags: reject(tag => tag === action.tag, state.filteredTags),
};
case TAG_EDITED:
const renameTag = tag => tag === action.oldName ? action.newName : tag;
return {
...state,
tags: state.tags.map(
tag => tag === action.oldName ? action.newName : tag
).sort(),
// FIXME This should be optimized somehow...
tags: state.tags.map(renameTag).sort(),
filteredTags: state.filteredTags.map(renameTag).sort(),
};
case FILTER_TAGS:
return {
...state,
filteredTags: state.tags.filter(
tag => tag.toLowerCase().match(action.searchTerm),
),
};
default:
return state;
@ -61,3 +74,8 @@ export const _listTags = ShlinkApiClient => async dispatch => {
}
};
export const listTags = () => _listTags(ShlinkApiClient);
export const filterTags = searchTerm => ({
type: FILTER_TAGS,
searchTerm,
});

View file

@ -2,10 +2,17 @@ import React from 'react';
import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import searchIcon from '@fortawesome/fontawesome-free-solid/faSearch';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import './SearchField.scss';
const propTypes = {
onChange: PropTypes.func.isRequired,
className: PropTypes.string,
placeholder: PropTypes.string,
};
const defaultProps = {
className: '',
placeholder: 'Search...',
};
export default class SearchField extends React.Component {
@ -31,12 +38,14 @@ export default class SearchField extends React.Component {
}
render() {
const { className, placeholder } = this.props;
return (
<div className="search-field">
<div className={classnames('search-field', className)}>
<input
type="text"
className="form-control form-control-lg search-field__input"
placeholder="Search..."
placeholder={placeholder}
onChange={e => this.searchTermChanged(e.target.value)}
value={this.state.searchTerm}
/>
@ -55,3 +64,4 @@ export default class SearchField extends React.Component {
}
SearchField.propTypes = propTypes;
SearchField.defaultProps = defaultProps;