diff --git a/package-lock.json b/package-lock.json
index 6063c83f..2d73c4b3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6578,15 +6578,6 @@
"csstype": "^3.0.2"
}
},
- "@types/react-autosuggest": {
- "version": "10.1.2",
- "resolved": "https://registry.npmjs.org/@types/react-autosuggest/-/react-autosuggest-10.1.2.tgz",
- "integrity": "sha512-K23lmXhC3Bbd8y/jm5+wYrw/NAeN4U/wlHTgAEBIwLOyQKFCFYA3ONKte9P21L+RGIXRP8UlzHOSRtmIZw5Nqw==",
- "dev": true,
- "requires": {
- "@types/react": "*"
- }
- },
"@types/react-color": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.4.tgz",
@@ -6669,10 +6660,10 @@
"@types/react-router": "*"
}
},
- "@types/react-tagsinput": {
- "version": "3.19.7",
- "resolved": "https://registry.npmjs.org/@types/react-tagsinput/-/react-tagsinput-3.19.7.tgz",
- "integrity": "sha512-yj/3iFBLoan/0vzXMxC9zGhO1uJ89qjQldekf0o3fX4mYdaAPW/VbP921fsyYt6PdHmJ9UMo+kERSMzUAml1xQ==",
+ "@types/react-tag-autocomplete": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@types/react-tag-autocomplete/-/react-tag-autocomplete-6.1.0.tgz",
+ "integrity": "sha512-6qJQS81ZMaqV/ZSADwiU91TXnR6ZJINPqoV3z2SMMSlUcO6CV8Vc5QnqcqcVTj2CHnU3UQ2Q5QfSj3NyXomcDg==",
"dev": true,
"requires": {
"@types/react": "*"
@@ -12919,7 +12910,8 @@
"es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
- "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
+ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
+ "dev": true
},
"escalade": {
"version": "3.1.1",
@@ -24575,18 +24567,6 @@
}
}
},
- "react-autosuggest": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/react-autosuggest/-/react-autosuggest-10.1.0.tgz",
- "integrity": "sha512-/azBHmc6z/31s/lBf6irxPf/7eejQdR0IqnZUzjdSibtlS8+Rw/R79pgDAo6Ft5QqCUTyEQ+f0FhL+1olDQ8OA==",
- "requires": {
- "es6-promise": "^4.2.8",
- "prop-types": "^15.7.2",
- "react-themeable": "^1.1.0",
- "section-iterator": "^2.0.0",
- "shallow-equal": "^1.2.1"
- }
- },
"react-chartjs-2": {
"version": "2.11.1",
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-2.11.1.tgz",
@@ -24998,25 +24978,10 @@
"resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-6.0.1.tgz",
"integrity": "sha512-69nonicgjT4ofeHxZSpjuz37BoIiWMEbUYkX0mdTCY2mX1U53XDzDUIOVKRg6vVBNGL+pxYjbRzmylXWORh1xQ=="
},
- "react-tagsinput": {
- "version": "3.19.0",
- "resolved": "https://registry.yarnpkg.com/react-tagsinput/-/react-tagsinput-3.19.0.tgz",
- "integrity": "sha1-bjtFWV8tKV1GV78ZRJGYj5SMqr8="
- },
- "react-themeable": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz",
- "integrity": "sha1-fURm3ZsrX6dQWHJ4JenxUro3mg4=",
- "requires": {
- "object-assign": "^3.0.0"
- },
- "dependencies": {
- "object-assign": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
- "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I="
- }
- }
+ "react-tag-autocomplete": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-6.1.0.tgz",
+ "integrity": "sha512-AMhVqxEEIrOfzH0A9XrpsTaLZCVYgjjxp3DSTuSvx91LBSFI6uYcKe38ltR/H/TQw4aytofVghQ1hR9sKpXRQA=="
},
"react-transition-group": {
"version": "2.9.0",
@@ -26121,11 +26086,6 @@
}
}
},
- "section-iterator": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz",
- "integrity": "sha1-v0RNev7rlK1Dw5rS+yYVFifMuio="
- },
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -26374,11 +26334,6 @@
"safe-buffer": "^5.0.1"
}
},
- "shallow-equal": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
- "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
- },
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
diff --git a/package.json b/package.json
index 911cbd65..a6c00e59 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,6 @@
"qs": "^6.9.6",
"ramda": "^0.27.1",
"react": "^17.0.1",
- "react-autosuggest": "^10.1.0",
"react-chartjs-2": "^2.11.1",
"react-color": "^2.19.3",
"react-copy-to-clipboard": "^5.0.2",
@@ -51,7 +50,7 @@
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"react-swipeable": "^6.0.1",
- "react-tagsinput": "^3.19.0",
+ "react-tag-autocomplete": "^6.1.0",
"reactstrap": "^8.9.0",
"redux": "^4.0.5",
"redux-localstorage-simple": "^2.4.0",
@@ -80,7 +79,6 @@
"@types/qs": "^6.9.5",
"@types/ramda": "^0.27.38",
"@types/react": "^17.0.2",
- "@types/react-autosuggest": "^10.1.2",
"@types/react-color": "^3.0.4",
"@types/react-copy-to-clipboard": "^5.0.0",
"@types/react-datepicker": "^3.1.5",
@@ -88,7 +86,7 @@
"@types/react-leaflet": "^2.5.2",
"@types/react-redux": "^7.1.16",
"@types/react-router-dom": "^5.1.7",
- "@types/react-tagsinput": "^3.19.7",
+ "@types/react-tag-autocomplete": "^6.1.0",
"@types/uuid": "^8.3.0",
"@wojtekmaj/enzyme-adapter-react-17": "^0.3.1",
"adm-zip": "^0.4.16",
diff --git a/src/common/react-tag-autocomplete.scss b/src/common/react-tag-autocomplete.scss
new file mode 100644
index 00000000..b41d7b7c
--- /dev/null
+++ b/src/common/react-tag-autocomplete.scss
@@ -0,0 +1,145 @@
+@import '../utils/base';
+
+.react-tags {
+ position: relative;
+ padding: 5px 0 0 6px;
+ border-radius: .3rem;
+ background-color: var(--input-color);
+ border: 1px solid var(--input-border-color);
+ transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
+
+ /* shared font styles */
+ font-size: 1em;
+ line-height: 1.2;
+
+ /* clicking anywhere will focus the input */
+ cursor: text;
+}
+
+.react-tags.is-focused {
+ box-shadow: 0 0 0 .2rem rgb(70 150 229 / 25%);
+}
+
+.react-tags__tag {
+ font-size: 100%;
+}
+
+.react-tags__selected {
+ display: inline;
+ vertical-align: 2px;
+}
+
+.react-tags__selected-tag {
+ display: inline-block;
+ box-sizing: border-box;
+ margin: 0 6px 6px 0;
+ padding: 6px 8px;
+ border: 1px solid var(--input-border-color);
+ border-radius: .25rem;
+ background: #f1f1f1;
+
+ /* match the font styles */
+ font-size: inherit;
+ line-height: inherit;
+}
+
+.react-tags__selected-tag:after {
+ content: '\2715';
+ color: #aaaaaa;
+ margin-left: 8px;
+}
+
+.react-tags__selected-tag:hover,
+.react-tags__selected-tag:focus {
+ border-color: var(--input-border-color);
+}
+
+.react-tags__search {
+ display: inline-block;
+
+ /* match tag layout */
+ padding: 6px 2px;
+ margin-bottom: 5px;
+
+ /* prevent autoresize overflowing the container */
+ max-width: 100%;
+}
+
+@media screen and (min-width: $smMin) {
+ .react-tags__search {
+ /* this will become the offsetParent for suggestions */
+ position: relative;
+ }
+}
+
+.react-tags__search-input {
+ font-size: 1.25rem;
+ line-height: inherit;
+ color: var(--input-text-color);
+ background-color: var(--input-color);
+
+ /* prevent autoresize overflowing the container */
+ max-width: 100%;
+
+ /* remove styles and layout from this element */
+ margin: 0 0 0 7px;
+ padding: 0;
+ border: 0;
+ outline: none;
+}
+
+.react-tags__search-input::-ms-clear {
+ display: none;
+}
+
+.react-tags__suggestions {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 100%;
+ z-index: 10;
+}
+
+@media screen and (min-width: $smMin) {
+ .react-tags__suggestions {
+ width: 240px;
+ }
+}
+
+.react-tags__suggestions ul {
+ margin: 4px -1px;
+ padding: 0;
+ list-style: none;
+ background: var(--primary-color);
+ border: 1px solid var(--border-color);
+ border-radius: .25rem;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, .2);
+}
+
+.react-tags__suggestions li {
+ padding: 8px 10px;
+}
+
+.react-tags__suggestions li:not(:last-child) {
+ border-bottom: 1px solid var(--border-color);
+}
+
+.react-tags__suggestions li mark {
+ text-decoration: underline;
+ background: none;
+ font-weight: 600;
+}
+
+.react-tags__suggestions li:hover {
+ cursor: pointer;
+ background-color: var(--active-color);
+}
+
+.react-tags__suggestions li.is-active {
+ background-color: var(--active-color);
+}
+
+.react-tags__suggestions li.is-disabled {
+ opacity: .5;
+ cursor: auto;
+}
diff --git a/src/common/react-tagsinput.scss b/src/common/react-tagsinput.scss
deleted file mode 100644
index 6ecd1cd3..00000000
--- a/src/common/react-tagsinput.scss
+++ /dev/null
@@ -1,58 +0,0 @@
-@import '../utils/base';
-
-.react-tagsinput {
- background-color: var(--input-color);
- border: 1px solid var(--input-border-color);
- border-radius: .25rem;
- overflow: hidden;
- min-height: 2.6rem;
- padding: .5rem 0 0 1rem;
- transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
-}
-
-.react-tagsinput--focused {
- border-color: #80bdff;
- box-shadow: 0 0 0 .2rem rgb(70 150 229 / 25%);
-}
-
-.react-tagsinput-tag {
- font-size: 1rem;
- background-color: #f1f1f1;
- border-radius: 4px;
- display: inline-block;
- font-weight: 400;
- margin: 0 5px 6px 0;
- padding: 6px 8px;
- line-height: 1;
- color: #ffffff;
-}
-
-.react-tagsinput-remove {
- cursor: pointer;
- font-weight: 700;
- margin-left: 8px;
-}
-
-.react-tagsinput-tag span:before {
- content: '\2715';
- color: #ffffff;
-}
-
-.react-tagsinput-input {
- background: transparent;
- border: 0;
- outline: none;
- padding: 1px 0;
- width: 100%;
- margin-bottom: 6px;
- font-size: 1.25rem;
- color: var(--input-text-color);
-}
-
-.react-tagsinput-input::placeholder {
- color: $textPlaceholder;
-}
-
-.react-autosuggest__suggestion--highlighted {
- background-color: var(--active-color);
-}
diff --git a/src/index.scss b/src/index.scss
index 5bcc10ae..5a330475 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -2,7 +2,7 @@
@import './utils/base';
@import 'node_modules/bootstrap/scss/bootstrap.scss';
-@import './common/react-tagsinput.scss';
+@import './common/react-tag-autocomplete.scss';
@import './theme/theme';
* {
diff --git a/src/short-urls/ShortUrlForm.tsx b/src/short-urls/ShortUrlForm.tsx
index 7fcfba3e..368da62e 100644
--- a/src/short-urls/ShortUrlForm.tsx
+++ b/src/short-urls/ShortUrlForm.tsx
@@ -97,7 +97,7 @@ export const ShortUrlForm = (
-
+
>
);
diff --git a/src/tags/helpers/Tag.tsx b/src/tags/helpers/Tag.tsx
index cf6da951..895caded 100644
--- a/src/tags/helpers/Tag.tsx
+++ b/src/tags/helpers/Tag.tsx
@@ -1,18 +1,19 @@
-import { FC } from 'react';
+import { FC, MouseEventHandler } from 'react';
import ColorGenerator from '../../utils/services/ColorGenerator';
import './Tag.scss';
interface TagProps {
colorGenerator: ColorGenerator;
text: string;
+ className?: string;
clearable?: boolean;
- onClick?: () => void;
- onClose?: () => void;
+ onClick?: MouseEventHandler;
+ onClose?: MouseEventHandler;
}
-const Tag: FC = ({ text, children, clearable, colorGenerator, onClick, onClose }) => (
+const Tag: FC = ({ text, children, clearable, className = '', colorGenerator, onClick, onClose }) => (
diff --git a/src/tags/helpers/TagsSelector.scss b/src/tags/helpers/TagsSelector.scss
deleted file mode 100644
index aba09e1b..00000000
--- a/src/tags/helpers/TagsSelector.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-@import '../../utils/base';
-
-.react-autosuggest__suggestions-list {
- list-style-type: none;
- padding: 0;
- margin-bottom: 6px;
-}
-
-.react-autosuggest__suggestion {
- margin-left: -6px;
- padding: 5px 8px;
-}
-
-.react-autosuggest__suggestion--highlighted {
- background-color: $lightGrey;
-}
diff --git a/src/tags/helpers/TagsSelector.tsx b/src/tags/helpers/TagsSelector.tsx
index 1611ac03..f3a64002 100644
--- a/src/tags/helpers/TagsSelector.tsx
+++ b/src/tags/helpers/TagsSelector.tsx
@@ -1,13 +1,12 @@
-import { ChangeEvent, useEffect } from 'react';
-import TagsInput, { RenderInputProps, RenderTagProps } from 'react-tagsinput';
-import Autosuggest, { ChangeEvent as AutoChangeEvent, SuggestionSelectedEventData } from 'react-autosuggest';
+import { useEffect } from 'react';
+import ReactTags, { SuggestionComponentProps, TagComponentProps } from 'react-tag-autocomplete';
import ColorGenerator from '../../utils/services/ColorGenerator';
import { TagsList } from '../reducers/tagsList';
import TagBullet from './TagBullet';
-import './TagsSelector.scss';
+import Tag from './Tag';
export interface TagsSelectorProps {
- tags: string[];
+ selectedTags: string[];
onChange: (tags: string[]) => void;
placeholder?: string;
}
@@ -17,65 +16,42 @@ interface TagsSelectorConnectProps extends TagsSelectorProps {
tagsList: TagsList;
}
-const noop = () => {};
+const toComponentTag = (tag: string) => ({ id: tag, name: tag });
const TagsSelector = (colorGenerator: ColorGenerator) => (
- { tags, onChange, listTags, tagsList, placeholder = 'Add tags to the URL' }: TagsSelectorConnectProps,
+ { selectedTags, onChange, listTags, tagsList, placeholder = 'Add tags to the URL' }: TagsSelectorConnectProps,
) => {
useEffect(() => {
listTags();
}, []);
- const renderTag = (
- { tag, key, disabled, onRemove, classNameRemove, getTagDisplayValue, ...other }: RenderTagProps,
- ) => (
-
- {getTagDisplayValue(tag)}
- {!disabled && onRemove(key)} />}
-
+ const renderTag = ({ tag, onDelete }: TagComponentProps) =>
+ ;
+ const renderSuggestion = ({ item }: SuggestionComponentProps) => (
+ <>
+
+ {item.name}
+ >
);
- const renderAutocompleteInput = (data: RenderInputProps) => {
- const { addTag, ...otherProps } = data;
- const handleOnChange = (e: ChangeEvent, { method }: AutoChangeEvent) => {
- method === 'enter' ? e.preventDefault() : otherProps.onChange(e);
- };
-
- const inputValue = otherProps.value?.trim().toLowerCase() ?? '';
- const suggestions = tagsList.tags.filter((tag) => tag.startsWith(inputValue));
-
- return (
- value.trim().length > 0}
- getSuggestionValue={(suggestion) => suggestion}
- renderSuggestion={(suggestion) => (
- <>
-
- {suggestion}
- >
- )}
- onSuggestionsFetchRequested={noop}
- onSuggestionsClearRequested={noop}
- onSuggestionSelected={(_, { suggestion }: SuggestionSelectedEventData) => {
- addTag(suggestion);
- }}
- />
- );
- };
return (
- !selectedTags.includes(tag)).map(toComponentTag)}
+ suggestionComponent={renderSuggestion}
+ allowNew
+ placeholderText={placeholder}
+ onDelete={(removedTagIndex) => {
+ selectedTags.splice(removedTagIndex, 1);
+
+ onChange(selectedTags);
+ }}
+ onAddition={({ name: newTag }) => {
+ const tags = [ ...selectedTags, newTag.toLowerCase() ]; // eslint-disable-line @typescript-eslint/no-unsafe-call
+
+ onChange(tags);
+ }}
/>
);
};