Created filtering by tag feature

This commit is contained in:
Alejandro Celaya 2018-08-01 19:04:58 +02:00
parent eef1946243
commit bbeaf01319
6 changed files with 92 additions and 47 deletions

View file

@ -2,6 +2,7 @@ import searchIcon from '@fortawesome/fontawesome-free-solid/faSearch';
import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import React from 'react';
import { connect } from 'react-redux';
import Tag from '../utils/Tag';
import { listShortUrls } from './reducers/shortUrlsList';
import './SearchBar.scss';
import { pick } from 'ramda';
@ -14,22 +15,41 @@ export class SearchBar extends React.Component {
timer = null;
render() {
const { listShortUrls, shortUrlsListParams } = this.props;
const selectedTag = shortUrlsListParams.tags ? shortUrlsListParams.tags[0] : '';
return (
<div className="search-bar">
<input type="text"
className="form-control form-control-lg search-bar__input"
placeholder="Search..."
onChange={e => this.searchTermChanged(e.target.value)}
value={this.state.searchTerm}
/>
<FontAwesomeIcon icon={searchIcon} className="search-bar__icon" />
<div className="close search-bar__close"
hidden={! this.state.showClearBtn}
onClick={() => this.searchTermChanged('')}
id="search-bar__close"
>
&times;
<div class="serach-bar-container">
<div className="search-bar">
<input
type="text"
className="form-control form-control-lg search-bar__input"
placeholder="Search..."
onChange={e => this.searchTermChanged(e.target.value)}
value={this.state.searchTerm}
/>
<FontAwesomeIcon icon={searchIcon} className="search-bar__icon" />
<div
className="close search-bar__close"
hidden={! this.state.showClearBtn}
onClick={() => this.searchTermChanged('')}
id="search-bar__close"
>
&times;
</div>
</div>
{selectedTag && (
<h4 className="search-bar__selected-tag mt-2">
<small>Filtering by tag:</small>
&nbsp;
<Tag
text={selectedTag}
clearable
onClose={() => listShortUrls({ ...shortUrlsListParams, tags: undefined })}
/>
</h4>
)}
</div>
);
}

View file

@ -1,14 +1,12 @@
import caretDownIcon from '@fortawesome/fontawesome-free-solid/faCaretDown';
import caretUpIcon from '@fortawesome/fontawesome-free-solid/faCaretUp';
import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import { isEmpty } from 'ramda';
import { isEmpty, pick } from 'ramda';
import React from 'react';
import { connect } from 'react-redux';
import Tag from '../utils/Tag';
import { ShortUrlsRow } from './helpers/ShortUrlsRow';
import { listShortUrls } from './reducers/shortUrlsList';
import './ShortUrlsList.scss';
import { pick } from 'ramda';
export class ShortUrlsList extends React.Component {
refreshList = extraParams => {
@ -110,17 +108,14 @@ export class ShortUrlsList extends React.Component {
}
return shortUrlsList.map(shortUrl => (
<ShortUrlsRow shortUrl={shortUrl} selectedServer={selectedServer} key={shortUrl.shortCode} />
<ShortUrlsRow
shortUrl={shortUrl}
selectedServer={selectedServer}
key={shortUrl.shortCode}
refreshList={this.refreshList}
/>
));
}
static renderTags(tags) {
if (isEmpty(tags)) {
return <i className="nowrap"><small>No tags</small></i>;
}
return tags.map(tag => <Tag text={tag} />);
}
}
export default connect(pick(['selectedServer', 'shortUrlsListParams']), { listShortUrls })(ShortUrlsList);

View file

@ -1,12 +1,22 @@
import { isEmpty } from 'ramda';
import React from 'react';
import Moment from 'react-moment';
import { ShortUrlsList } from '../ShortUrlsList';
import Tag from '../../utils/Tag';
import './ShortUrlsRow.scss';
import { ShortUrlsRowMenu } from './ShortUrlsRowMenu';
import './ShortUrlsRow.scss'
export class ShortUrlsRow extends React.Component {
state = { displayMenu: false, copiedToClipboard: false };
renderTags(tags) {
if (isEmpty(tags)) {
return <i className="nowrap"><small>No tags</small></i>;
}
const { refreshList } = this.props;
return tags.map(tag => <Tag key={tag} text={tag} onClick={() => refreshList({ tags: [tag] })} />);
}
render() {
const { shortUrl, selectedServer } = this.props;
const completeShortUrl = !selectedServer ? shortUrl.shortCode : `${selectedServer.url}/${shortUrl.shortCode}`;
@ -22,18 +32,18 @@ export class ShortUrlsRow extends React.Component {
<td className="short-urls-row__cell">
<a href={completeShortUrl} target="_blank">{completeShortUrl}</a>
</td>
<td className="short-urls-row__cell short-urls-row__cell--relative">
<td className="short-urls-row__cell">
<a href={shortUrl.originalUrl} target="_blank">{shortUrl.originalUrl}</a>
</td>
<td className="short-urls-row__cell">{this.renderTags(shortUrl.tags)}</td>
<td className="short-urls-row__cell text-right">{shortUrl.visitsCount}</td>
<td className="short-urls-row__cell short-urls-row__cell--relative">
<small
className="badge badge-warning short-urls-row__copy-hint"
hidden={!this.state.copiedToClipboard}
>
Copied short URL!
</small>
</td>
<td className="short-urls-row__cell">{ShortUrlsList.renderTags(shortUrl.tags)}</td>
<td className="short-urls-row__cell text-right">{shortUrl.visitsCount}</td>
<td className="short-urls-row__cell">
<ShortUrlsRowMenu
display={this.state.displayMenu}
shortUrl={completeShortUrl}

View file

@ -10,5 +10,5 @@
.short-urls-row__copy-hint {
@include vertical-align();
right: 10px;
right: 100%;
}

View file

@ -2,21 +2,27 @@ import React from 'react';
import ColorGenerator from '../utils/ColorGenerator';
import './Tag.scss';
export default class Tag extends React.Component {
constructor(props) {
super(props);
this.colorGenerator = props.ColorGenerator;
}
render() {
return (
<span className="badge tag" style={{ backgroundColor: this.colorGenerator.getColorForKey(this.props.text) }}>
{this.props.text}
</span>
);
export default function Tag (
{
colorGenerator,
text,
clearable,
onClick = () => ({}),
onClose = () => ({})
}
) {
return (
<span
className="badge tag"
style={{ backgroundColor: colorGenerator.getColorForKey(text), cursor: clearable ? 'auto' : 'pointer' }}
onClick={onClick}
>
{text}
{clearable && <span className="close tag__close-selected-tag" onClick={onClose}>&times;</span>}
</span>
);
}
Tag.defaultProps = {
ColorGenerator
colorGenerator: ColorGenerator
};

View file

@ -1,7 +1,21 @@
.tag {
color: #fff;
cursor: pointer;
}
.tag:not(:last-child) {
margin-right: 3px;
}
.tag__close-selected-tag.tag__close-selected-tag {
font-size: inherit;
color: inherit;
opacity: 1;
cursor: pointer;
margin-left: 5px;
}
.tag__close-selected-tag.tag__close-selected-tag:hover {
color: inherit;
opacity: 1;
}