-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAddTags.tsx
More file actions
143 lines (129 loc) · 4.27 KB
/
Copy pathAddTags.tsx
File metadata and controls
143 lines (129 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"use client";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { Cross1Icon } from "@radix-ui/react-icons";
import { tags } from "./constants";
type AddTagsPropsType = {};
export const AddTags: React.FC<AddTagsPropsType> = () => {
const [searchQuery, setSearchQuery] = useState("");
const [selectedTags, setSelectedTags] = useState<Array<string>>([]);
const [isInputFocused, setIsInputFocused] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null);
const suggestionsRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
suggestionsRef.current &&
!suggestionsRef.current.contains(event.target as Node) &&
inputRef.current &&
!inputRef.current.contains(event.target as Node)
) {
setIsInputFocused(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
useEffect(() => {
const handleKeyListener = (event: KeyboardEvent) => {
if (searchQuery.trim().length === 0 && event.key === "Backspace") {
if (selectedTags && selectedTags.length > 0) {
const updatedTags = selectedTags.slice(0, -1);
setSelectedTags(updatedTags);
}
}
};
window.addEventListener("keydown", handleKeyListener);
return () => {
window.removeEventListener("keydown", handleKeyListener);
};
}, [searchQuery, selectedTags]);
const filteredTags = useMemo(() => {
const lowerCaseQuery = searchQuery.toLocaleLowerCase();
const filtered = tags.filter(
(tag) =>
tag.toLocaleLowerCase().includes(lowerCaseQuery) &&
!(selectedTags && selectedTags.includes(tag))
);
return filtered;
}, [searchQuery, selectedTags]);
const handleAddTag = useCallback(
(tag: string) => {
const updatedTags = selectedTags ? [...selectedTags, tag] : [tag];
setSelectedTags(updatedTags);
setSearchQuery("");
setIsInputFocused(false);
},
[selectedTags]
);
const handleRemoveTag = useCallback(
(tag: string) => {
const updatedTags = selectedTags?.filter((item) => item !== tag);
setSelectedTags(updatedTags);
setIsInputFocused(false);
},
[selectedTags]
);
return (
<div className="relative">
<div className="bg-gray-100 dark:bg-gray-800 rounded-md shadow-md flex gap-2 flex-wrap p-1.5">
{selectedTags.map((tag) => (
<div
key={tag}
className="bg-gray-200 dark:bg-gray-950 p-1.5 rounded-md flex gap-2 items-center shadow-md"
>
{tag}
<button
onClick={() => handleRemoveTag(tag)}
aria-label={`Remove tag ${tag}`}
>
<Cross1Icon height={10} width={10} className="ml-1" />
</button>
</div>
))}
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="cursor-text bg-transparent focus:outline-none flex-1 p-1.5 rounded-md w-20"
ref={inputRef}
placeholder="+ Add tag"
onFocus={() => {
setIsInputFocused(true);
}}
/>
</div>
{isInputFocused && (
<div className="flex w-full justify-center items-center">
<div
ref={suggestionsRef}
className="bg-gray-100 dark:bg-gray-800 mt-6 shadow-lg p-1.5 w-60 rounded-md max-h-40 overflow-scroll"
>
{filteredTags.length > 0 ? (
<div className="flex flex-col gap-1">
{filteredTags.map((tag) => (
<button
key={tag}
onClick={() => handleAddTag(tag)}
className="hover:bg-gray-200 hover:dark:bg-gray-950 focus:bg-gray-200 focus:dark:bg-gray-950 focus:outline-none w-full p-2 rounded-md flex justify-start"
>
{tag}
</button>
))}
</div>
) : (
<div className="p-1">No tags found</div>
)}
</div>
</div>
)}
</div>
);
};