From 854851fefc78a41750fde8e70445db3fe4ee516b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 15 Aug 2018 11:14:44 +0200 Subject: [PATCH] Created common component to handle tags and modal to edit tags --- src/common/react-tagsinput.scss | 11 +++--- src/short-urls/CreateShortUrl.js | 10 ++---- src/short-urls/ShortUrlsList.js | 16 +++++---- src/short-urls/helpers/EditTagsModal.js | 27 ++++++++++++++ src/short-urls/helpers/QrCodeModal.js | 2 +- src/short-urls/helpers/ShortUrlsRow.js | 4 +-- src/short-urls/helpers/ShortUrlsRowMenu.js | 29 +++++++++++---- src/utils/ColorGenerator.js | 5 +++ src/utils/TagsSelector.js | 41 ++++++++++++++++++++++ 9 files changed, 115 insertions(+), 30 deletions(-) create mode 100644 src/short-urls/helpers/EditTagsModal.js create mode 100644 src/utils/TagsSelector.js diff --git a/src/common/react-tagsinput.scss b/src/common/react-tagsinput.scss index 2bddbf33..ef4cc8f4 100644 --- a/src/common/react-tagsinput.scss +++ b/src/common/react-tagsinput.scss @@ -3,7 +3,7 @@ border: 1px solid #ccc; border-radius: .25rem; overflow: hidden; - min-height: calc(2.6rem + 2px); + min-height: 2.6rem; padding: 6px 0 0 6px; } @@ -21,16 +21,13 @@ .react-tagsinput-tag { font-size: 1rem; background-color: #f1f1f1; - border-radius: 2px; - border: 1px solid #d1d1d1; + border-radius: 4px; display: inline-block; font-weight: 400; margin: 0 5px 6px 0; padding: 6px 8px; line-height: 1; -} -.react-tagsinput-tag:hover { - border-color: #b1b1b1; + color: #fff; } .react-tagsinput-remove { @@ -41,7 +38,7 @@ .react-tagsinput-tag a::before { content: "\2715"; - color: #aaa; + color: #fff; } .react-tagsinput-input { diff --git a/src/short-urls/CreateShortUrl.js b/src/short-urls/CreateShortUrl.js index 59655469..2a5abae1 100644 --- a/src/short-urls/CreateShortUrl.js +++ b/src/short-urls/CreateShortUrl.js @@ -4,11 +4,11 @@ import FontAwesomeIcon from '@fortawesome/react-fontawesome'; import { assoc, dissoc, isNil, pick, pipe, replace, trim } from 'ramda'; import React from 'react'; import { connect } from 'react-redux'; -import TagsInput from 'react-tagsinput' import { Collapse } from 'reactstrap'; import DateInput from '../common/DateInput'; import CreateShortUrlResult from './helpers/CreateShortUrlResult'; import { createShortUrl, resetCreateShortUrl } from './reducers/shortUrlCreationResult'; +import TagsSelector from '../utils/TagsSelector'; export class CreateShortUrl extends React.Component { state = { @@ -68,13 +68,7 @@ export class CreateShortUrl extends React.Component {
- +
diff --git a/src/short-urls/ShortUrlsList.js b/src/short-urls/ShortUrlsList.js index ef997589..2a1a68d5 100644 --- a/src/short-urls/ShortUrlsList.js +++ b/src/short-urls/ShortUrlsList.js @@ -1,7 +1,7 @@ import caretDownIcon from '@fortawesome/fontawesome-free-solid/faCaretDown' import caretUpIcon from '@fortawesome/fontawesome-free-solid/faCaretUp' import FontAwesomeIcon from '@fortawesome/react-fontawesome' -import { head, isEmpty, pick, toPairs } from 'ramda' +import { head, isEmpty, pick, toPairs, keys, values } from 'ramda' import React from 'react' import { connect } from 'react-redux' import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown } from 'reactstrap' @@ -34,7 +34,7 @@ export class ShortUrlsList extends React.Component { '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 }); @@ -58,8 +58,8 @@ export class ShortUrlsList extends React.Component { const { orderBy } = props.shortUrlsListParams; this.state = { - orderField: orderBy ? head(Object.keys(orderBy)) : undefined, - orderDir: orderBy ? head(Object.values(orderBy)) : undefined, + orderField: orderBy ? head(keys(orderBy)) : undefined, + orderDir: orderBy ? head(values(orderBy)) : undefined, } } @@ -71,7 +71,11 @@ export class ShortUrlsList extends React.Component { renderShortUrls() { const { shortUrlsList, selectedServer, loading, error, shortUrlsListParams } = this.props; if (error) { - return Something went wrong while loading short URLs :(; + return ( + + Something went wrong while loading short URLs :( + + ); } if (loading) { @@ -102,7 +106,7 @@ export class ShortUrlsList extends React.Component { {toPairs(SORTABLE_FIELDS).map(([key, value]) => - this.orderBy(key)}> + this.orderBy(key)}> {value} {this.renderOrderIcon(key, 'short-urls-list__header-icon--mobile')} )} diff --git a/src/short-urls/helpers/EditTagsModal.js b/src/short-urls/helpers/EditTagsModal.js new file mode 100644 index 00000000..e85292e8 --- /dev/null +++ b/src/short-urls/helpers/EditTagsModal.js @@ -0,0 +1,27 @@ +import React from 'react'; +import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import TagsSelector from '../../utils/TagsSelector'; + +export default class EditTagsModal extends React.Component { + constructor(props) { + super(props); + this.state = { tags: props.shortUrl.tags }; + } + + render() { + const { isOpen, toggle, url } = this.props; + const changeTags = tags => this.setState({ tags }); + + return ( + + Edit tags for {url} + + + + + + + + ); + } +} diff --git a/src/short-urls/helpers/QrCodeModal.js b/src/short-urls/helpers/QrCodeModal.js index 1cedabba..194443f2 100644 --- a/src/short-urls/helpers/QrCodeModal.js +++ b/src/short-urls/helpers/QrCodeModal.js @@ -4,7 +4,7 @@ import './QrCodeModal.scss'; export default function QrCodeModal ({ url, toggle, isOpen }) { return ( - + QR code for {url}
diff --git a/src/short-urls/helpers/ShortUrlsRow.js b/src/short-urls/helpers/ShortUrlsRow.js index d3534e45..0237bc07 100644 --- a/src/short-urls/helpers/ShortUrlsRow.js +++ b/src/short-urls/helpers/ShortUrlsRow.js @@ -45,9 +45,9 @@ export class ShortUrlsRow extends React.Component { Copied short URL! { this.setState({ copiedToClipboard: true }); setTimeout(() => this.setState({ copiedToClipboard: false }), 2000); diff --git a/src/short-urls/helpers/ShortUrlsRowMenu.js b/src/short-urls/helpers/ShortUrlsRowMenu.js index 9e05c9a2..916736d2 100644 --- a/src/short-urls/helpers/ShortUrlsRowMenu.js +++ b/src/short-urls/helpers/ShortUrlsRowMenu.js @@ -1,5 +1,6 @@ import copyIcon from '@fortawesome/fontawesome-free-regular/faCopy'; import pictureIcon from '@fortawesome/fontawesome-free-regular/faImage'; +import tagsIcon from '@fortawesome/fontawesome-free-solid/faTags'; import pieChartIcon from '@fortawesome/fontawesome-free-solid/faChartPie'; import menuIcon from '@fortawesome/fontawesome-free-solid/faEllipsisV'; import qrIcon from '@fortawesome/fontawesome-free-solid/faQrcode'; @@ -11,16 +12,23 @@ import { ButtonDropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reac import PreviewModal from './PreviewModal'; import QrCodeModal from './QrCodeModal'; import './ShortUrlsRowMenu.scss'; +import EditTagsModal from './EditTagsModal'; export class ShortUrlsRowMenu extends React.Component { - state = { isOpen: false, isQrModalOpen: false, isPreviewOpen: false }; + state = { + isOpen: false, + isQrModalOpen: false, + isPreviewOpen: false, + isTagsModalOpen: false, + }; toggle = () => this.setState({ isOpen: !this.state.isOpen }); render() { - const { shortUrl, onCopyToClipboard, selectedServer, shortCode } = this.props; + const { completeShortUrl, onCopyToClipboard, selectedServer, shortUrl } = this.props; const serverId = selectedServer ? selectedServer.id : ''; const toggleQrCode = () => this.setState({isQrModalOpen: !this.state.isQrModalOpen}); const togglePreview = () => this.setState({isPreviewOpen: !this.state.isPreviewOpen}); + const toggleTags = () => this.setState({isTagsModalOpen: !this.state.isTagsModalOpen}); return ( @@ -28,9 +36,18 @@ export class ShortUrlsRowMenu extends React.Component {    - +  Visit Stats + +  Edit tags + + @@ -38,7 +55,7 @@ export class ShortUrlsRowMenu extends React.Component {  Preview @@ -47,14 +64,14 @@ export class ShortUrlsRowMenu extends React.Component {  QR code - +  Copy to clipboard diff --git a/src/utils/ColorGenerator.js b/src/utils/ColorGenerator.js index 30e041a8..840b485b 100644 --- a/src/utils/ColorGenerator.js +++ b/src/utils/ColorGenerator.js @@ -1,4 +1,5 @@ import Storage from './Storage'; +import PropTypes from 'prop-types'; const buildRandomColor = () => { const letters = '0123456789ABCDEF'; @@ -29,4 +30,8 @@ export class ColorGenerator { }; } +export const colorGeneratorType = PropTypes.shape({ + getColorForKey: PropTypes.func, +}); + export default new ColorGenerator(Storage); diff --git a/src/utils/TagsSelector.js b/src/utils/TagsSelector.js new file mode 100644 index 00000000..865f6d78 --- /dev/null +++ b/src/utils/TagsSelector.js @@ -0,0 +1,41 @@ +import React from 'react'; +import TagsInput from 'react-tagsinput'; +import ColorGenerator, { colorGeneratorType } from './ColorGenerator'; +import PropTypes from 'prop-types'; + +const defaultProps = { + colorGenerator: ColorGenerator, + placeholder: 'Add tags to the URL', +}; +const propTypes = { + tags: PropTypes.arrayOf(PropTypes.string).isRequired, + onChange: PropTypes.func.isRequired, + placeholder: PropTypes.string, + colorGenerator: colorGeneratorType +}; + +export default function TagsSelector({ tags, onChange, placeholder, colorGenerator }) { + const renderTag = (props) => { + const { tag, key, disabled, onRemove, classNameRemove, getTagDisplayValue, ...other } = props; + return ( + + {getTagDisplayValue(tag)} + {!disabled && onRemove(key)} />} + + ) + }; + + return ( + + ); +} + +TagsSelector.defaultProps = defaultProps; +TagsSelector.propTypes = propTypes;