@@ -6,10 +6,7 @@ function GlobalTaskForm({ onAdd, onCancel, availableTags = [] }) {
66 const [ text , setText ] = useState ( '' ) ;
77 const [ selectedTags , setSelectedTags ] = useState ( [ ] ) ;
88 const [ newTagInput , setNewTagInput ] = useState ( '' ) ;
9- const [ showTagSelector , setShowTagSelector ] = useState ( false ) ;
109 const inputRef = useRef ( null ) ;
11- const tagInputRef = useRef ( null ) ;
12- const dropdownRef = useRef ( null ) ;
1310
1411 useEffect ( ( ) => {
1512 // Auto-focus input when component mounts
@@ -22,59 +19,48 @@ function GlobalTaskForm({ onAdd, onCancel, availableTags = [] }) {
2219 e . preventDefault ( ) ;
2320 if ( ! text . trim ( ) ) return ;
2421
25- onAdd ( { text, isCompleted : false , tags : selectedTags } ) ;
22+ // Add current tag input if it exists and not already added
23+ const finalTagInput = newTagInput . trim ( ) ;
24+ if ( finalTagInput && ! selectedTags . includes ( finalTagInput ) ) {
25+ onAdd ( { text, isCompleted : false , tags : [ ...selectedTags , finalTagInput ] } ) ;
26+ } else {
27+ onAdd ( { text, isCompleted : false , tags : selectedTags } ) ;
28+ }
29+
2630 setText ( '' ) ;
2731 setSelectedTags ( [ ] ) ;
2832 setNewTagInput ( '' ) ;
2933 } ;
3034
31- const toggleTag = ( tag ) => {
32- if ( selectedTags . includes ( tag ) ) {
33- setSelectedTags ( selectedTags . filter ( t => t !== tag ) ) ;
34- } else {
35- setSelectedTags ( [ ...selectedTags , tag ] ) ;
36- }
35+ const addTag = ( tag ) => {
36+ if ( ! tag . trim ( ) || selectedTags . includes ( tag . trim ( ) ) ) return ;
37+ setSelectedTags ( [ ...selectedTags , tag . trim ( ) ] ) ;
38+ setNewTagInput ( '' ) ;
3739 } ;
3840
3941 const removeTag = ( tag ) => {
4042 setSelectedTags ( selectedTags . filter ( t => t !== tag ) ) ;
4143 } ;
4244
43- const addNewTag = ( ) => {
44- const tagToAdd = newTagInput . trim ( ) ;
45- if ( ! tagToAdd ) return ;
46-
47- if ( ! selectedTags . includes ( tagToAdd ) ) {
48- setSelectedTags ( [ ...selectedTags , tagToAdd ] ) ;
45+ // Handle input key events (add tag on Enter or comma)
46+ const handleTagKeyDown = ( e ) => {
47+ if ( ( e . key === 'Enter' || e . key === ',' ) && newTagInput . trim ( ) ) {
48+ e . preventDefault ( ) ;
49+ addTag ( newTagInput . trim ( ) ) ;
4950 }
50-
51- setNewTagInput ( '' ) ;
5251 } ;
5352
54- // Filter available tags based on search input
55- const getFilteredAvailableTags = ( ) => {
56- const searchLower = newTagInput . toLowerCase ( ) ;
53+ // Get matching tags based on the current input
54+ const getMatchingTags = ( ) => {
55+ if ( ! newTagInput . trim ( ) ) return [ ] ;
56+
57+ const inputLower = newTagInput . toLowerCase ( ) ;
5758 return availableTags . filter ( tag =>
5859 ! selectedTags . includes ( tag ) &&
59- tag . toLowerCase ( ) . includes ( searchLower )
60+ tag . toLowerCase ( ) . includes ( inputLower )
6061 ) ;
6162 } ;
6263
63- const handleClickOutside = ( e ) => {
64- if ( dropdownRef . current && ! dropdownRef . current . contains ( e . target ) &&
65- tagInputRef . current && ! tagInputRef . current . contains ( e . target ) ) {
66- setShowTagSelector ( false ) ;
67- }
68- } ;
69-
70- // Add event listener for clicking outside
71- useEffect ( ( ) => {
72- document . addEventListener ( 'mousedown' , handleClickOutside ) ;
73- return ( ) => {
74- document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
75- } ;
76- } , [ ] ) ;
77-
7864 return (
7965 < form className = "global-task-form mb-6" onSubmit = { onSubmit } >
8066 < div className = "relative mb-4" >
@@ -111,74 +97,50 @@ function GlobalTaskForm({ onAdd, onCancel, availableTags = [] }) {
11197 </ div >
11298 </ div >
11399
114- { /* Tag selector */ }
115- < div className = "relative mb-3" >
116- < div className = "absolute left-3 top-1/2 -translate-y-1/2" >
117- < TagIcon className = "h-4 w-4 text-neutral-500" />
100+ { /* Tag input field */ }
101+ < div className = "mb-3" >
102+ < div className = "relative" >
103+ < div className = "absolute left-3 top-1/2 -translate-y-1/2" >
104+ < TagIcon className = "h-4 w-4 text-neutral-500" />
105+ </ div >
106+ < input
107+ type = "text"
108+ placeholder = "Enter a new tag (press Enter or comma to add)"
109+ value = { newTagInput }
110+ onChange = { ( e ) => setNewTagInput ( e . target . value ) }
111+ onKeyDown = { handleTagKeyDown }
112+ className = "w-full py-3 px-4 pl-9 text-sm text-neutral-800 rounded-lg border border-neutral-200 focus:border-primary-400 focus:ring-1 focus:ring-primary-200 outline-none transition-all"
113+ autoComplete = "off"
114+ />
115+
116+ { /* Add button */ }
117+ { newTagInput . trim ( ) && ! selectedTags . includes ( newTagInput . trim ( ) ) && (
118+ < button
119+ type = "button"
120+ onClick = { ( ) => addTag ( newTagInput . trim ( ) ) }
121+ className = "absolute right-2 top-1/2 -translate-y-1/2 p-1 bg-primary-500 hover:bg-primary-600 text-white rounded-full"
122+ >
123+ < PlusIcon className = "h-4 w-4" />
124+ </ button >
125+ ) }
118126 </ div >
119- < input
120- ref = { tagInputRef }
121- type = "text"
122- placeholder = { selectedTags . length > 0 ? "Add more tags..." : "Add tags (e.g. work, urgent)" }
123- value = { newTagInput }
124- onChange = { ( e ) => setNewTagInput ( e . target . value ) }
125- onFocus = { ( ) => setShowTagSelector ( true ) }
126- className = "w-full py-3 px-4 pl-9 text-sm text-neutral-800 rounded-lg border border-neutral-200 focus:border-primary-400 focus:ring-1 focus:ring-primary-200 outline-none transition-all"
127- autoComplete = "off"
128- />
129127
130- { /* Tag selector dropdown */ }
131- { showTagSelector && (
132- < div
133- ref = { dropdownRef }
134- className = "absolute z-50 left-0 right-0 mt-2 bg-white rounded-lg shadow-xl border border-neutral-200 max-h-64 overflow-y-auto"
135- >
136- < div className = "py-1" >
137- < div className = "sticky top-0 px-4 py-2.5 bg-neutral-50 border-b border-neutral-200" >
138- < span className = "text-sm font-medium text-neutral-700" > Select Tags</ span >
139- </ div >
140-
141- { /* Add new tag option */ }
142- { newTagInput . trim ( ) && ! availableTags . includes ( newTagInput . trim ( ) ) && ! selectedTags . includes ( newTagInput . trim ( ) ) && (
143- < div
144- className = "flex items-center justify-between px-4 py-3 text-sm hover:bg-green-50 cursor-pointer border-b border-neutral-100"
145- onClick = { addNewTag }
128+ { /* Matching tag suggestions - only shown when input matches existing tags */ }
129+ { getMatchingTags ( ) . length > 0 && (
130+ < div className = "mt-2" >
131+ < p className = "text-xs font-medium text-neutral-500 mb-1.5" > Select matching tag:</ p >
132+ < div className = "flex flex-wrap gap-2" >
133+ { getMatchingTags ( ) . map ( ( tag , index ) => (
134+ < button
135+ key = { index }
136+ type = "button"
137+ onClick = { ( ) => addTag ( tag ) }
138+ className = "inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-primary-50 text-primary-700 hover:bg-primary-100 transition-colors"
146139 >
147- < div className = "flex items-center" >
148- < PlusIcon className = "h-4 w-4 mr-2 text-green-600" />
149- < span className = "font-medium text-green-700" > Create tag: "{ newTagInput . trim ( ) } "</ span >
150- </ div >
151- </ div >
152- ) }
153-
154- { /* Existing tags list */ }
155- { getFilteredAvailableTags ( ) . length > 0 ? (
156- < div className = "py-1" >
157- { getFilteredAvailableTags ( ) . map ( ( tag , index ) => (
158- < div
159- key = { index }
160- className = "flex items-center justify-between px-4 py-3 text-sm hover:bg-primary-50 cursor-pointer transition-colors"
161- onClick = { ( ) => toggleTag ( tag ) }
162- >
163- < div className = "flex items-center" >
164- < TagIcon className = "h-4 w-4 mr-2.5 text-primary-600" />
165- < span className = "font-medium text-neutral-800" > { tag } </ span >
166- </ div >
167- < span className = "text-xs px-2 py-1 bg-primary-50 rounded text-primary-700" > Click to add</ span >
168- </ div >
169- ) ) }
170- </ div >
171- ) : newTagInput && (
172- < div className = "px-4 py-4 text-sm text-neutral-500 text-center" >
173- No matching tags
174- </ div >
175- ) }
176-
177- { ! newTagInput && availableTags . length === 0 && (
178- < div className = "px-4 py-4 text-sm text-neutral-500 text-center" >
179- No tags available. Type to create a new tag.
180- </ div >
181- ) }
140+ < TagIcon className = "h-3 w-3 mr-1" />
141+ { tag }
142+ </ button >
143+ ) ) }
182144 </ div >
183145 </ div >
184146 ) }
@@ -187,18 +149,18 @@ function GlobalTaskForm({ onAdd, onCancel, availableTags = [] }) {
187149 { /* Selected tags display */ }
188150 { selectedTags . length > 0 && (
189151 < div className = "mb-1" >
190- < p className = "text-xs text-neutral-500 mb-2" > Selected tags:</ p >
191- < div className = "flex flex-wrap gap-2" >
152+ < p className = "text-xs font-medium text-neutral-500 mb-2" > Selected tags:</ p >
153+ < div className = "flex flex-wrap gap-2 p-2 bg-neutral-50 rounded-lg " >
192154 { selectedTags . map ( ( tag , index ) => (
193155 < div
194156 key = { index }
195- className = "inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-primary-100 text-primary-700 group"
157+ className = "inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-primary-500 text-white group"
196158 >
197159 < TagIcon className = "h-3 w-3 mr-1" />
198160 < span > { tag } </ span >
199161 < button
200162 type = "button"
201- className = "ml-1 p-0.5 rounded-full hover:bg-primary-200 group-hover:text-primary-800 "
163+ className = "ml-1 p-0.5 rounded-full hover:bg-primary-600 group-hover:text-white "
202164 onClick = { ( e ) => {
203165 e . preventDefault ( ) ;
204166 removeTag ( tag ) ;
0 commit comments