Skip to content

Commit

Permalink
Feat: add rest api support (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-sumi-k authored Feb 27, 2024
1 parent 38ac483 commit ba02f4e
Show file tree
Hide file tree
Showing 7 changed files with 669 additions and 443 deletions.
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<p align="center"><a href="https://canopas.com/contact"><img src="./assets/banner.png"></a></p>

<h1><strong>Tagsinput plugin for strapi</strong></h1>
<h1><strong>Tagsinput plugin for strapi with suggestions</strong></h1>

This plugin is used to add tagsinput in your strapi admin panel.
Read more about it at [tagsinput guidence](https://blog.canopas.com/the-simple-guidance-how-to-add-tagsinput-customfield-plugin-in-strapi-b5d2b5af7c3b).
Expand All @@ -21,6 +21,30 @@ Using yarn,
yarn add strapi-plugin-tagsinput
```

## How to use

After installation, you can add tagsinput as custom field.

#### Suggestions for tag

While adding tagsInput, you will see `API URL` field.

If you want to use REST API for suggestions, then add your API url in this field.

**Notes:**

- If API domain is different, then full API URL is required. i.e `http://localhost:1337/api/v1/tags?fields[0]=name` (Make sure API CORS are enabled for your strapi domain in this case).
- Otherwise add only path of API i.e `/api/v1/tags?fields[0]=name`
- API response should contain `name` field.
For example,

```
[
{ name: "tag1" },
{ name: "tag2" }
]
```

## Showcase

How to use tagsinput?
Expand Down
99 changes: 95 additions & 4 deletions admin/src/components/Input/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import TagsInput from "react-tagsinput";
import React, { useState } from "react";
import Autosuggest from "react-autosuggest";
import React, { useState, useRef, useEffect } from "react";
import PropTypes from "prop-types";
import "../styles/global.css";
import axios from "axios";

import {
Flex,
Expand Down Expand Up @@ -32,6 +34,30 @@ const Tags = ({
return [];
}
});
const [suggestions, setSuggestions] = useState([]);
let inputEle = useRef(null);

useEffect(() => {
document.getElementsByClassName(
"react-autosuggest__suggestions-container"
)[0].style.top = inputEle.current.offsetHeight + 5 + "px";

function handleClickOutside(event) {
if (inputEle.current && !inputEle.current.contains(event.target)) {
document
.getElementsByClassName("react-tagsinput")[0]
.classList.remove("react-tagsinput--focused");
} else {
document
.getElementsByClassName("react-tagsinput")[0]
.classList.add("react-tagsinput--focused");
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
});

const handleTagsChange = (tags) => {
setTags(tags);
Expand All @@ -44,6 +70,59 @@ const Tags = ({
});
};

const getSuggestions = async () => {
if (!attribute.options || !attribute.options["apiUrl"]) {
return [];
}
try {
const res = await axios.get(attribute.options["apiUrl"]);
setSuggestions(res.data);
} catch (err) {
console.log(err);
}
};

const autocompleteRenderInput = (props) => {
const handleOnChange = (e, { newValue, method }) => {
if (method === "enter") {
e.preventDefault();
} else {
props.onChange(e);
}
};

const inputValue = (props.value && props.value.trim().toLowerCase()) || "";
const inputLength = inputValue.length;

let s = suggestions;

if (suggestions.length <= 0) {
getSuggestions();
}

if (inputLength > 0) {
s = suggestions.filter((state) => {
return state.name.toLowerCase().slice(0, inputLength) === inputValue;
});
}

return (
<Autosuggest
ref={props.ref}
suggestions={s}
shouldRenderSuggestions={(value) => value && value.trim().length > 0}
getSuggestionValue={(s) => s.name}
renderSuggestion={(s) => <span>{s.name}</span>}
inputProps={{ ...props, onChange: handleOnChange }}
onSuggestionSelected={(e, { suggestion }) => {
props.addTag(suggestion.name);
}}
onSuggestionsClearRequested={() => this.setTags([])}
onSuggestionsFetchRequested={() => {}}
/>
);
};

return (
<Field
name={name}
Expand All @@ -52,10 +131,22 @@ const Tags = ({
error={error}
hint={description && formatMessage(description)}
required={required}>
<Flex direction="column" alignItems="stretch" gap={1}>
<Flex
direction="column"
alignItems="stretch"
gap={1}
style={{
position: `relative`,
}}
ref={inputEle}>
<FieldLabel action={labelAction}>{formatMessage(intlLabel)}</FieldLabel>
<Flex>
<TagsInput value={tags} onChange={handleTagsChange} />
<Flex direction="column">
<TagsInput
value={tags}
onChange={handleTagsChange}
onlyUnique={true}
renderInput={autocompleteRenderInput}
/>
</Flex>
<FieldHint />
<FieldError />
Expand Down
37 changes: 36 additions & 1 deletion admin/src/components/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,40 @@
margin-top: 1px;
outline: none;
padding: 5px;
width: 80px;
width: 100%;
}

.react-tagsinput > span {
display: flex;
flex-flow: wrap;
}

.react-autosuggest__container {
display: flex;
flex-direction: column;
flex: auto;
}

.react-autosuggest__suggestions-container {
position: absolute;
z-index: 200;
width: 280px;
margin: 0;
padding: 0;
list-style-type: none;
background-color: #fff;
}

.react-autosuggest__suggestions-container--open {
border: 1px solid #aaa;
}

.react-autosuggest__suggestion {
cursor: pointer;
padding: 10px 20px;
}

.react-autosuggest__suggestion--highlighted,
.react-autosuggest__suggestion--focused {
background-color: #ccc;
}
22 changes: 22 additions & 0 deletions admin/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,28 @@ export default {
/* webpackChunkName: "input-component" */ "./components/Input"
),
},
options: {
base: [
{
sectionTitle: {
id: "tagsinput.tags.section.apiUrl",
defaultMessage: "API Url",
},
items: [
{
intlLabel: {
id: "tagsinput.tags.section.apiUrl",
defaultMessage: "Rest API URL for suggestions",
},
name: "options.apiUrl",
type: "text",
value: "",
options: [],
},
],
},
],
},
});
},
};
Binary file modified assets/showcase.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "strapi-plugin-tagsinput",
"version": "1.0.3",
"version": "1.0.4",
"description": "Tagsinput plugin for your strapi project",
"strapi": {
"name": "tagsinput",
Expand All @@ -18,12 +18,14 @@
"@strapi/helper-plugin": "^4.20.2",
"@strapi/icons": "^1.14.1",
"prop-types": "^15.8.1",
"react-autosuggest": "^10.1.0",
"react-tagsinput": "^3.20.3"
},
"devDependencies": {
"@babel/cli": "^7.23.9",
"@babel/core": "^7.23.9",
"@babel/preset-env": "^7.23.9",
"axios": "^1.6.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^5.3.4",
Expand Down
Loading

0 comments on commit ba02f4e

Please sign in to comment.