mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-03 23:07:26 +03:00
Added ordering control to short URLs list in mobile resolutions
This commit is contained in:
parent
e4d5424c07
commit
c80fea2877
2 changed files with 119 additions and 81 deletions
|
@ -1,12 +1,20 @@
|
||||||
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 { head, isEmpty, pick } from 'ramda';
|
import { head, isEmpty, pick, toPairs } from 'ramda'
|
||||||
import React from 'react';
|
import React from 'react'
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux'
|
||||||
import { ShortUrlsRow } from './helpers/ShortUrlsRow';
|
import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown } from 'reactstrap'
|
||||||
import { listShortUrls } from './reducers/shortUrlsList';
|
import { ShortUrlsRow } from './helpers/ShortUrlsRow'
|
||||||
import './ShortUrlsList.scss';
|
import { listShortUrls } from './reducers/shortUrlsList'
|
||||||
|
import './ShortUrlsList.scss'
|
||||||
|
|
||||||
|
const SORTABLE_FIELDS = {
|
||||||
|
dateCreated: 'Created at',
|
||||||
|
shortCode: 'Short URL',
|
||||||
|
originalUrl: 'Long URL',
|
||||||
|
visits: 'Visits',
|
||||||
|
};
|
||||||
|
|
||||||
export class ShortUrlsList extends React.Component {
|
export class ShortUrlsList extends React.Component {
|
||||||
refreshList = extraParams => {
|
refreshList = extraParams => {
|
||||||
|
@ -16,6 +24,34 @@ export class ShortUrlsList extends React.Component {
|
||||||
...extraParams
|
...extraParams
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
determineOrderDir = field => {
|
||||||
|
if (this.state.orderField !== field) {
|
||||||
|
return 'ASC';
|
||||||
|
}
|
||||||
|
|
||||||
|
const newOrderMap = {
|
||||||
|
'ASC': 'DESC',
|
||||||
|
'DESC': undefined,
|
||||||
|
};
|
||||||
|
return this.state.orderDir ? newOrderMap[this.state.orderDir] : 'ASC';
|
||||||
|
}
|
||||||
|
orderBy = field => {
|
||||||
|
const newOrderDir = this.determineOrderDir(field);
|
||||||
|
this.setState({ orderField: newOrderDir !== undefined ? field : undefined, orderDir: newOrderDir });
|
||||||
|
this.refreshList({ orderBy: { [field]: newOrderDir } })
|
||||||
|
};
|
||||||
|
renderOrderIcon = (field, className = 'short-urls-list__header-icon') => {
|
||||||
|
if (this.state.orderField !== field) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={this.state.orderDir === 'ASC' ? caretUpIcon : caretDownIcon}
|
||||||
|
className={className}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -32,78 +68,6 @@ export class ShortUrlsList extends React.Component {
|
||||||
this.refreshList({ page: params.page });
|
this.refreshList({ page: params.page });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
const determineOrderDir = field => {
|
|
||||||
if (this.state.orderField !== field) {
|
|
||||||
return 'ASC';
|
|
||||||
}
|
|
||||||
|
|
||||||
const newOrderMap = {
|
|
||||||
'ASC': 'DESC',
|
|
||||||
'DESC': undefined,
|
|
||||||
};
|
|
||||||
return this.state.orderDir ? newOrderMap[this.state.orderDir] : 'ASC';
|
|
||||||
}
|
|
||||||
const orderBy = field => {
|
|
||||||
const newOrderDir = determineOrderDir(field);
|
|
||||||
this.setState({ orderField: field, orderDir: newOrderDir });
|
|
||||||
this.refreshList({ orderBy: { [field]: newOrderDir } })
|
|
||||||
};
|
|
||||||
const renderOrderIcon = field => {
|
|
||||||
if (this.state.orderField !== field || this.state.orderDir === undefined) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={this.state.orderDir === 'ASC' ? caretUpIcon : caretDownIcon}
|
|
||||||
className="short-urls-list__header-icon"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<table className="table table-striped table-hover">
|
|
||||||
<thead className="short-urls-list__header">
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
|
|
||||||
onClick={() => orderBy('dateCreated')}
|
|
||||||
>
|
|
||||||
{renderOrderIcon('dateCreated')}
|
|
||||||
Created at
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
|
|
||||||
onClick={() => orderBy('shortCode')}
|
|
||||||
>
|
|
||||||
{renderOrderIcon('shortCode')}
|
|
||||||
Short URL
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
|
|
||||||
onClick={() => orderBy('originalUrl')}
|
|
||||||
>
|
|
||||||
{renderOrderIcon('originalUrl')}
|
|
||||||
Long URL
|
|
||||||
</th>
|
|
||||||
<th className="short-urls-list__header-cell">Tags</th>
|
|
||||||
<th
|
|
||||||
className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
|
|
||||||
onClick={() => orderBy('visits')}
|
|
||||||
>
|
|
||||||
<span className="nowrap">{renderOrderIcon('visits')} Visits</span>
|
|
||||||
</th>
|
|
||||||
<th className="short-urls-list__header-cell"> </th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{this.renderShortUrls()}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderShortUrls() {
|
renderShortUrls() {
|
||||||
const { shortUrlsList, selectedServer, loading, error, shortUrlsListParams } = this.props;
|
const { shortUrlsList, selectedServer, loading, error, shortUrlsListParams } = this.props;
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -128,6 +92,71 @@ export class ShortUrlsList extends React.Component {
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderMobileOrderingControls() {
|
||||||
|
return (
|
||||||
|
<div className="d-block d-md-none mb-3">
|
||||||
|
<UncontrolledDropdown>
|
||||||
|
<DropdownToggle caret className="btn-block">
|
||||||
|
Order by
|
||||||
|
</DropdownToggle>
|
||||||
|
<DropdownMenu className="short-urls-list__order-dropdown">
|
||||||
|
{toPairs(SORTABLE_FIELDS).map(([key, value]) =>
|
||||||
|
<DropdownItem active={this.state.orderField === key} onClick={() => this.orderBy(key)}>
|
||||||
|
{value}
|
||||||
|
{this.renderOrderIcon(key, 'short-urls-list__header-icon--mobile')}
|
||||||
|
</DropdownItem>)}
|
||||||
|
</DropdownMenu>
|
||||||
|
</UncontrolledDropdown>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{this.renderMobileOrderingControls()}
|
||||||
|
<table className="table table-striped table-hover">
|
||||||
|
<thead className="short-urls-list__header">
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
|
||||||
|
onClick={() => this.orderBy('dateCreated')}
|
||||||
|
>
|
||||||
|
{this.renderOrderIcon('dateCreated')}
|
||||||
|
Created at
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
|
||||||
|
onClick={() => this.orderBy('shortCode')}
|
||||||
|
>
|
||||||
|
{this.renderOrderIcon('shortCode')}
|
||||||
|
Short URL
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
|
||||||
|
onClick={() => this.orderBy('originalUrl')}
|
||||||
|
>
|
||||||
|
{this.renderOrderIcon('originalUrl')}
|
||||||
|
Long URL
|
||||||
|
</th>
|
||||||
|
<th className="short-urls-list__header-cell">Tags</th>
|
||||||
|
<th
|
||||||
|
className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
|
||||||
|
onClick={() => this.orderBy('visits')}
|
||||||
|
>
|
||||||
|
<span className="nowrap">{this.renderOrderIcon('visits')} Visits</span>
|
||||||
|
</th>
|
||||||
|
<th className="short-urls-list__header-cell"> </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this.renderShortUrls()}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(pick(['selectedServer', 'shortUrlsListParams']), { listShortUrls })(ShortUrlsList);
|
export default connect(pick(['selectedServer', 'shortUrlsListParams']), { listShortUrls })(ShortUrlsList);
|
||||||
|
|
|
@ -14,6 +14,15 @@
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.short-urls-list__header-icon--mobile {
|
||||||
|
margin: 3.5px 0 0;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
.short-urls-list__header-cell--with-action {
|
.short-urls-list__header-cell--with-action {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.short-urls-list__order-dropdown {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue