mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-04-20 13:36:20 +00:00
Updated styles in javascript to fulfill adidas rules
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
import Storage from './Storage';
|
||||
import PropTypes from 'prop-types';
|
||||
import { range } from 'ramda';
|
||||
import PropTypes from 'prop-types';
|
||||
import storage from './Storage';
|
||||
|
||||
const HEX_COLOR_LENGTH = 6;
|
||||
const { floor, random } = Math;
|
||||
const letters = '0123456789ABCDEF';
|
||||
const buildRandomColor = () =>
|
||||
`#${
|
||||
range(0, 6)
|
||||
.map(() => letters[floor(random() * 16)])
|
||||
range(0, HEX_COLOR_LENGTH)
|
||||
.map(() => letters[floor(random() * letters.length)])
|
||||
.join('')
|
||||
}`;
|
||||
|
||||
@@ -17,12 +18,13 @@ export class ColorGenerator {
|
||||
this.colors = this.storage.get('colors') || {};
|
||||
}
|
||||
|
||||
getColorForKey = key => {
|
||||
getColorForKey = (key) => {
|
||||
const color = this.colors[key];
|
||||
|
||||
// If a color has not been set yet, generate a random one and save it
|
||||
if (!color) {
|
||||
this.setColorForKey(key, buildRandomColor());
|
||||
|
||||
return this.getColorForKey(key);
|
||||
}
|
||||
|
||||
@@ -40,4 +42,6 @@ export const colorGeneratorType = PropTypes.shape({
|
||||
setColorForKey: PropTypes.func,
|
||||
});
|
||||
|
||||
export default new ColorGenerator(Storage);
|
||||
const colorGenerator = new ColorGenerator(storage);
|
||||
|
||||
export default colorGenerator;
|
||||
|
||||
19
src/utils/ExternalLink.js
Normal file
19
src/utils/ExternalLink.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const propTypes = {
|
||||
href: PropTypes.string.isRequired,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default function ExternalLink(props) {
|
||||
const { href, children, ...rest } = props;
|
||||
|
||||
return (
|
||||
<a target="_blank" rel="noopener noreferrer" href={href} {...rest}>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
ExternalLink.propTypes = propTypes;
|
||||
@@ -1,8 +1,15 @@
|
||||
import React from 'react';
|
||||
import { Card } from 'reactstrap';
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function MutedMessage({ children, marginSize = 4 }) {
|
||||
const DEFAULT_MARGIN_SIZE = 4;
|
||||
const propTypes = {
|
||||
marginSize: PropTypes.number,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default function MutedMessage({ children, marginSize = DEFAULT_MARGIN_SIZE }) {
|
||||
const cardClasses = classnames('bg-light', {
|
||||
[`mt-${marginSize}`]: marginSize > 0,
|
||||
});
|
||||
@@ -17,3 +24,5 @@ export default function MutedMessage({ children, marginSize = 4 }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
MutedMessage.propTypes = propTypes;
|
||||
|
||||
@@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import './SearchField.scss';
|
||||
|
||||
const DEFAULT_SEARCH_INTERVAL = 500;
|
||||
const propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
className: PropTypes.string,
|
||||
@@ -19,7 +20,7 @@ export default class SearchField extends React.Component {
|
||||
state = { showClearBtn: false, searchTerm: '' };
|
||||
timer = null;
|
||||
|
||||
searchTermChanged(searchTerm, timeout = 500) {
|
||||
searchTermChanged(searchTerm, timeout = DEFAULT_SEARCH_INTERVAL) {
|
||||
this.setState({
|
||||
showClearBtn: searchTerm !== '',
|
||||
searchTerm,
|
||||
@@ -29,6 +30,7 @@ export default class SearchField extends React.Component {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
};
|
||||
|
||||
resetTimer();
|
||||
|
||||
this.timer = setTimeout(() => {
|
||||
@@ -46,15 +48,15 @@ export default class SearchField extends React.Component {
|
||||
type="text"
|
||||
className="form-control form-control-lg search-field__input"
|
||||
placeholder={placeholder}
|
||||
onChange={e => this.searchTermChanged(e.target.value)}
|
||||
value={this.state.searchTerm}
|
||||
onChange={(e) => this.searchTermChanged(e.target.value)}
|
||||
/>
|
||||
<FontAwesomeIcon icon={searchIcon} className="search-field__icon" />
|
||||
<div
|
||||
className="close search-field__close"
|
||||
hidden={! this.state.showClearBtn}
|
||||
onClick={() => this.searchTermChanged('', 0)}
|
||||
hidden={!this.state.showClearBtn}
|
||||
id="search-field__close"
|
||||
onClick={() => this.searchTermChanged('', 0)}
|
||||
>
|
||||
×
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
const PREFIX = 'shlink';
|
||||
const buildPath = path => `${PREFIX}.${path}`;
|
||||
const buildPath = (path) => `${PREFIX}.${path}`;
|
||||
|
||||
export class Storage {
|
||||
constructor(localStorage) {
|
||||
this.localStorage = localStorage;
|
||||
}
|
||||
|
||||
get = key => {
|
||||
get = (key) => {
|
||||
const item = this.localStorage.getItem(buildPath(key));
|
||||
|
||||
return item ? JSON.parse(item) : undefined;
|
||||
};
|
||||
|
||||
@@ -15,4 +16,5 @@ export class Storage {
|
||||
}
|
||||
|
||||
const storage = typeof localStorage !== 'undefined' ? localStorage : {};
|
||||
|
||||
export default new Storage(storage);
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
import React from 'react';
|
||||
import ColorGenerator from '../utils/ColorGenerator';
|
||||
import PropTypes from 'prop-types';
|
||||
import colorGenerator, { colorGeneratorType } from '../utils/ColorGenerator';
|
||||
import './Tag.scss';
|
||||
|
||||
export default function Tag (
|
||||
const propTypes = {
|
||||
colorGenerator: colorGeneratorType,
|
||||
text: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
clearable: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
const defaultProps = {
|
||||
colorGenerator,
|
||||
};
|
||||
|
||||
export default function Tag(
|
||||
{
|
||||
colorGenerator,
|
||||
text,
|
||||
children,
|
||||
clearable,
|
||||
onClick = () => ({}),
|
||||
onClose = () => ({})
|
||||
onClose = () => ({}),
|
||||
}
|
||||
) {
|
||||
return (
|
||||
@@ -24,6 +37,5 @@ export default function Tag (
|
||||
);
|
||||
}
|
||||
|
||||
Tag.defaultProps = {
|
||||
colorGenerator: ColorGenerator
|
||||
};
|
||||
Tag.defaultProps = defaultProps;
|
||||
Tag.propTypes = propTypes;
|
||||
|
||||
@@ -1,38 +1,41 @@
|
||||
import React from 'react';
|
||||
import TagsInput from 'react-tagsinput';
|
||||
import ColorGenerator, { colorGeneratorType } from './ColorGenerator';
|
||||
import PropTypes from 'prop-types';
|
||||
import colorGenerator, { colorGeneratorType } from './ColorGenerator';
|
||||
|
||||
const defaultProps = {
|
||||
colorGenerator: 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
|
||||
colorGenerator: colorGeneratorType,
|
||||
};
|
||||
|
||||
export default function TagsSelector({ tags, onChange, placeholder, colorGenerator }) {
|
||||
const renderTag = (props) => {
|
||||
const { tag, key, disabled, onRemove, classNameRemove, getTagDisplayValue, ...other } = props;
|
||||
|
||||
return (
|
||||
<span key={key} style={{ backgroundColor: colorGenerator.getColorForKey(tag) }} {...other}>
|
||||
{getTagDisplayValue(tag)}
|
||||
{!disabled && <span className={classNameRemove} onClick={() => onRemove(key)} />}
|
||||
</span>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<TagsInput
|
||||
value={tags}
|
||||
onChange={onChange}
|
||||
inputProps={{ placeholder }}
|
||||
onlyUnique
|
||||
addOnBlur // FIXME Workaround to be able to add tags on Android
|
||||
renderTag={renderTag}
|
||||
|
||||
// FIXME Workaround to be able to add tags on Android
|
||||
addOnBlur
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user