diff --git a/example/src/demos/CountrySelectorDnD.jsx b/example/src/demos/CountrySelectorDnD.jsx
new file mode 100644
index 0000000..436a9f9
--- /dev/null
+++ b/example/src/demos/CountrySelectorDnD.jsx
@@ -0,0 +1,186 @@
+import React, { useCallback, useState } from 'react'
+import { arrayMove, SortableContext, useSortable } from '@dnd-kit/sortable'
+import { CSS } from '@dnd-kit/utilities'
+import { ReactTags } from '../../../src'
+import { suggestions } from '../countries'
+import { closestCenter, DndContext } from '@dnd-kit/core'
+
+const tagToKey = (tag) => {
+ return `${String(tag.value)}-${tag.label}`
+}
+
+const Handle = (props) => {
+ const { isDragging, ...remainingProps } = props
+
+ return (
+
+
+
+ )
+}
+
+export function CountrySelectorDnD() {
+ const [selected, setSelected] = useState([suggestions[10], suggestions[121]])
+
+ const [options, setOptions] = useState({
+ activateFirstOption: false,
+ allowBackspace: false,
+ collapseOnSelect: false,
+ isDisabled: false,
+ isInvalid: false,
+ })
+
+ const onAdd = useCallback(
+ (newTag) => {
+ setSelected([...selected, newTag])
+ },
+ [selected]
+ )
+
+ const onDelete = useCallback(
+ (index) => {
+ setSelected(selected.filter((_, i) => i !== index))
+ },
+ [selected]
+ )
+
+ const onOptionChange = useCallback(
+ (e) => {
+ setOptions({ ...options, [e.target.name]: e.target.checked })
+ },
+ [options]
+ )
+
+ const handleDragEnd = (event) => {
+ const { active, over } = event
+
+ if (active.id !== over.id) {
+ setSelected((selected) => {
+ const oldIndex = selected.findIndex((s) => tagToKey(s) === active.id)
+ const newIndex = selected.findIndex((s) => tagToKey(s) === over.id)
+ return arrayMove(selected, oldIndex, newIndex)
+ })
+ }
+ }
+
+ const CustomTagList = ({ children, classNames, ...tagListprops }) => {
+ return (
+ child.key)}>
+
+ {React.Children.map(children, (child) => (
+ -
+ {child}
+
+ ))}
+
+
+ )
+ }
+
+ const CustomTag = ({ classNames, tag, ...tagProps }) => {
+ const { attributes, isDragging, listeners, setNodeRef, transform, transition } = useSortable({
+ id: tagToKey(tag),
+ })
+
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ }
+
+ return (
+
+
+
+ {tag.label}
+
+ onDelete(selected.findIndex((s) => tagToKey(s) === tagToKey(tag)))}
+ >
+
+ )
+ }
+
+ return (
+
+ Select the countries you have visited below:
+
+
+
+ View output
+
+ {JSON.stringify(selected, null, 2)}
+
+
+
+ )
+}
diff --git a/example/src/index.html b/example/src/index.html
index fec20d0..2a2dccb 100644
--- a/example/src/index.html
+++ b/example/src/index.html
@@ -156,6 +156,11 @@ Custom tag list component
+
+
Documentation
diff --git a/example/src/main.jsx b/example/src/main.jsx
index 8136d2e..339c78f 100644
--- a/example/src/main.jsx
+++ b/example/src/main.jsx
@@ -1,6 +1,7 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import { CountrySelector } from './demos/CountrySelector'
+import { CountrySelectorDnD } from './demos/CountrySelectorDnD'
import { CustomTags } from './demos/CustomTags'
import { CustomValidity } from './demos/CustomValidity'
import { AsyncSuggestions } from './demos/AsyncSuggestions'
@@ -27,4 +28,7 @@ window.onload = () => {
const container6 = ReactDOM.createRoot(document.getElementById('demo-6'))
container6.render()
+
+ const container7 = ReactDOM.createRoot(document.getElementById('demo-7'))
+ container7.render()
}
diff --git a/example/src/styles.css b/example/src/styles.css
index fec9c40..bdc3582 100644
--- a/example/src/styles.css
+++ b/example/src/styles.css
@@ -195,3 +195,63 @@
line-height: 1.5rem;
color: #00000080;
}
+
+.react-tags__dndtag {
+ margin: 0 0.25rem 0.25rem 0;
+ padding: 0.375rem 0.5rem;
+ border: 0;
+ border-radius: 3px;
+ background: #eaeef2;
+ /* match the font styles */
+ font-size: inherit;
+ line-height: inherit;
+}
+
+.react-tags__dndtag:hover {
+ color: #ffffff;
+ background-color: #4f46e5;
+}
+
+.react-tags__removeTag {
+ cursor: pointer;
+}
+
+.react-tags__handleTag {
+ margin-right: .25rem;
+ padding: .25rem 0.4rem;
+ border-radius: 5px;
+}
+
+.react-tags__handleTag:hover {
+ color: #ffffff;
+ background-color: rgba(255, 255, 255, .1);
+}
+
+
+.react-tags__removeTag::after {
+ content: '';
+ display: inline-block;
+ width: 0.65rem;
+ height: 0.65rem;
+ clip-path: polygon(
+ 10% 0,
+ 0 10%,
+ 40% 50%,
+ 0 90%,
+ 10% 100%,
+ 50% 60%,
+ 90% 100%,
+ 100% 90%,
+ 60% 50%,
+ 100% 10%,
+ 90% 0,
+ 50% 40%
+ );
+ margin-left: 0.5rem;
+ font-size: 0.875rem;
+ background-color: #7c7d86;
+}
+
+.react-tags__removeTag:hover::after {
+ background-color: #ffffff;
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 50cf713..abfcc10 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,9 @@
"version": "7.5.0",
"license": "ISC",
"devDependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/sortable": "^10.0.0",
+ "@dnd-kit/utilities": "^3.2.2",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.0",
@@ -514,6 +517,63 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
+ "node_modules/@dnd-kit/accessibility": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
+ "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/core": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
+ "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@dnd-kit/accessibility": "^3.1.1",
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/sortable": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
+ "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@dnd-kit/core": "^6.3.0",
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/utilities": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
+ "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@@ -4982,6 +5042,13 @@
"typescript": ">=4.2.0"
}
},
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/package.json b/package.json
index 30f32e0..8e68d4b 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,9 @@
},
"homepage": "https://github.com/i-like-robots/react-tag-autocomplete",
"devDependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/sortable": "^10.0.0",
+ "@dnd-kit/utilities": "^3.2.2",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.0",