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

View file

@ -1,14 +1,12 @@
import caretDownIcon from '@fortawesome/fontawesome-free-solid/faCaretDown'; import caretDownIcon from '@fortawesome/fontawesome-free-solid/faCaretDown';
import caretUpIcon from '@fortawesome/fontawesome-free-solid/faCaretUp'; import caretUpIcon from '@fortawesome/fontawesome-free-solid/faCaretUp';
import FontAwesomeIcon from '@fortawesome/react-fontawesome'; import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import { isEmpty } from 'ramda'; import { isEmpty, pick } from 'ramda';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Tag from '../utils/Tag';
import { ShortUrlsRow } from './helpers/ShortUrlsRow'; import { ShortUrlsRow } from './helpers/ShortUrlsRow';
import { listShortUrls } from './reducers/shortUrlsList'; import { listShortUrls } from './reducers/shortUrlsList';
import './ShortUrlsList.scss'; import './ShortUrlsList.scss';
import { pick } from 'ramda';
export class ShortUrlsList extends React.Component { export class ShortUrlsList extends React.Component {
refreshList = extraParams => { refreshList = extraParams => {
@ -110,17 +108,14 @@ export class ShortUrlsList extends React.Component {
} }
return shortUrlsList.map(shortUrl => ( 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); export default connect(pick(['selectedServer', 'shortUrlsListParams']), { listShortUrls })(ShortUrlsList);

View file

@ -1,12 +1,22 @@
import { isEmpty } from 'ramda';
import React from 'react'; import React from 'react';
import Moment from 'react-moment'; import Moment from 'react-moment';
import { ShortUrlsList } from '../ShortUrlsList'; import Tag from '../../utils/Tag';
import './ShortUrlsRow.scss';
import { ShortUrlsRowMenu } from './ShortUrlsRowMenu'; import { ShortUrlsRowMenu } from './ShortUrlsRowMenu';
import './ShortUrlsRow.scss'
export class ShortUrlsRow extends React.Component { export class ShortUrlsRow extends React.Component {
state = { displayMenu: false, copiedToClipboard: false }; 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() { render() {
const { shortUrl, selectedServer } = this.props; const { shortUrl, selectedServer } = this.props;
const completeShortUrl = !selectedServer ? shortUrl.shortCode : `${selectedServer.url}/${shortUrl.shortCode}`; 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"> <td className="short-urls-row__cell">
<a href={completeShortUrl} target="_blank">{completeShortUrl}</a> <a href={completeShortUrl} target="_blank">{completeShortUrl}</a>
</td> </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> <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 <small
className="badge badge-warning short-urls-row__copy-hint" className="badge badge-warning short-urls-row__copy-hint"
hidden={!this.state.copiedToClipboard} hidden={!this.state.copiedToClipboard}
> >
Copied short URL! Copied short URL!
</small> </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 <ShortUrlsRowMenu
display={this.state.displayMenu} display={this.state.displayMenu}
shortUrl={completeShortUrl} shortUrl={completeShortUrl}

View file

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

View file

@ -2,21 +2,27 @@ import React from 'react';
import ColorGenerator from '../utils/ColorGenerator'; import ColorGenerator from '../utils/ColorGenerator';
import './Tag.scss'; import './Tag.scss';
export default class Tag extends React.Component { export default function Tag (
constructor(props) { {
super(props); colorGenerator,
this.colorGenerator = props.ColorGenerator; text,
} clearable,
onClick = () => ({}),
render() { onClose = () => ({})
return (
<span className="badge tag" style={{ backgroundColor: this.colorGenerator.getColorForKey(this.props.text) }}>
{this.props.text}
</span>
);
} }
) {
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 = { Tag.defaultProps = {
ColorGenerator colorGenerator: ColorGenerator
}; };

View file

@ -1,7 +1,21 @@
.tag { .tag {
color: #fff; color: #fff;
cursor: pointer;
} }
.tag:not(:last-child) { .tag:not(:last-child) {
margin-right: 3px; 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;
}