mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 01:20:24 +03:00
Created filtering by tag feature
This commit is contained in:
parent
eef1946243
commit
bbeaf01319
6 changed files with 92 additions and 47 deletions
|
@ -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}
|
||||||
×
|
onClick={() => this.searchTermChanged('')}
|
||||||
|
id="search-bar__close"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{selectedTag && (
|
||||||
|
<h4 className="search-bar__selected-tag mt-2">
|
||||||
|
<small>Filtering by tag:</small>
|
||||||
|
|
||||||
|
<Tag
|
||||||
|
text={selectedTag}
|
||||||
|
clearable
|
||||||
|
onClose={() => listShortUrls({ ...shortUrlsListParams, tags: undefined })}
|
||||||
|
/>
|
||||||
|
</h4>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -10,5 +10,5 @@
|
||||||
|
|
||||||
.short-urls-row__copy-hint {
|
.short-urls-row__copy-hint {
|
||||||
@include vertical-align();
|
@include vertical-align();
|
||||||
right: 10px;
|
right: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}>×</span>}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tag.defaultProps = {
|
Tag.defaultProps = {
|
||||||
ColorGenerator
|
colorGenerator: ColorGenerator
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue