@@ -3,7 +3,7 @@ import { Badge } from '@/components/ui/badge';
33import { Button } from '@/components/ui/button' ;
44import { Input } from '@/components/ui/input' ;
55import { TagMultiSelectProps } from '@/components/utils/types' ;
6- import { ChevronDown , Plus } from 'lucide-react' ;
6+ import { ChevronDown , Plus , Check , X } from 'lucide-react' ;
77
88export const TagMultiSelect = ( {
99 availableTags,
@@ -12,8 +12,12 @@ export const TagMultiSelect = ({
1212 placeholder = 'Select or create tags' ,
1313 disabled = false ,
1414 className = '' ,
15+ autoOpen = false ,
16+ showActions = false ,
17+ onSave,
18+ onCancel,
1519} : TagMultiSelectProps ) => {
16- const [ isOpen , setIsOpen ] = useState ( false ) ;
20+ const [ isOpen , setIsOpen ] = useState ( autoOpen ) ;
1721 const [ searchTerm , setSearchTerm ] = useState ( '' ) ;
1822 const dropdownRef = useRef < HTMLDivElement > ( null ) ;
1923 const inputRef = useRef < HTMLInputElement > ( null ) ;
@@ -67,12 +71,15 @@ export const TagMultiSelect = ({
6771 const handleKeyDown = ( e : React . KeyboardEvent ) => {
6872 if ( e . key === 'Enter' ) {
6973 e . preventDefault ( ) ;
70- const filteredTags = getFilteredTags ( ) ;
71- if ( filteredTags . length > 0 ) {
72- handleTagSelect ( filteredTags [ 0 ] ) ;
73- } else if ( searchTerm . trim ( ) ) {
74- handleNewTagCreate ( ) ;
74+ if ( searchTerm . trim ( ) ) {
75+ const filteredTags = getFilteredTags ( ) ;
76+ if ( filteredTags . length > 0 ) {
77+ handleTagSelect ( filteredTags [ 0 ] ) ;
78+ } else {
79+ handleNewTagCreate ( ) ;
80+ }
7581 }
82+ // Do nothing if search is empty
7683 } else if ( e . key === 'Escape' ) {
7784 setIsOpen ( false ) ;
7885 setSearchTerm ( '' ) ;
@@ -92,6 +99,7 @@ export const TagMultiSelect = ({
9299 onClick = { ( ) => setIsOpen ( ! isOpen ) }
93100 disabled = { disabled }
94101 className = "w-full justify-between text-left font-normal"
102+ aria-label = "Select tags"
95103 >
96104 < span className = "truncate" >
97105 { selectedTags . length > 0
@@ -101,6 +109,50 @@ export const TagMultiSelect = ({
101109 < ChevronDown className = "h-4 w-4 opacity-50" />
102110 </ Button >
103111
112+ { /* Selected tags displayed below button with optional action buttons */ }
113+ { selectedTags . length > 0 && (
114+ < div className = "flex flex-wrap items-center gap-2 mt-2" >
115+ { selectedTags . map ( ( tag ) => (
116+ < Badge key = { tag } className = "flex items-center gap-1" >
117+ < span > { tag } </ span >
118+ < button
119+ type = "button"
120+ onClick = { ( ) => handleTagRemove ( tag ) }
121+ className = "ml-1 text-red-500 hover:text-red-700 text-xs"
122+ disabled = { disabled }
123+ aria-label = { `Remove ${ tag } ` }
124+ >
125+ ✖
126+ </ button >
127+ </ Badge >
128+ ) ) }
129+ { showActions && onSave && onCancel && (
130+ < div className = "flex items-center gap-1 whitespace-nowrap" >
131+ < Button
132+ type = "button"
133+ variant = "ghost"
134+ size = "icon"
135+ onClick = { onSave }
136+ className = "h-8 w-8"
137+ aria-label = "Save tags"
138+ >
139+ < Check className = "h-4 w-4 text-green-500" />
140+ </ Button >
141+ < Button
142+ type = "button"
143+ variant = "ghost"
144+ size = "icon"
145+ onClick = { onCancel }
146+ className = "h-8 w-8"
147+ aria-label = "Cancel"
148+ >
149+ < X className = "h-4 w-4 text-red-500" />
150+ </ Button >
151+ </ div >
152+ ) }
153+ </ div >
154+ ) }
155+
104156 { isOpen && (
105157 < div className = "absolute z-50 w-full mt-1 bg-background border border-border rounded-md shadow-lg max-h-60 overflow-hidden" >
106158 < div className = "p-2 border-b" >
@@ -119,16 +171,10 @@ export const TagMultiSelect = ({
119171 { getFilteredTags ( ) . map ( ( tag ) => (
120172 < div
121173 key = { tag }
122- className = "flex items-center gap-2 px-3 py-2 cursor-pointer hover:bg-accent transition-colors"
174+ className = "px-3 py-2 cursor-pointer hover:bg-accent transition-colors"
123175 onClick = { ( ) => handleTagSelect ( tag ) }
124176 >
125- < input
126- type = "checkbox"
127- checked = { false }
128- readOnly
129- className = "h-4 w-4"
130- />
131- < span className = "flex-1 text-sm" > { tag } </ span >
177+ < span className = "text-sm" > { tag } </ span >
132178 </ div >
133179 ) ) }
134180
@@ -152,28 +198,6 @@ export const TagMultiSelect = ({
152198 </ div >
153199 </ div >
154200 ) }
155-
156- { selectedTags . length > 0 && (
157- < div className = "flex flex-wrap gap-2 mt-2" >
158- { selectedTags . map ( ( tag ) => (
159- < Badge
160- key = { tag }
161- variant = "secondary"
162- className = "flex items-center gap-1"
163- >
164- < span > { tag } </ span >
165- < button
166- type = "button"
167- onClick = { ( ) => handleTagRemove ( tag ) }
168- className = "ml-1 text-red-500 hover:text-red-700 text-xs"
169- disabled = { disabled }
170- >
171- ✖
172- </ button >
173- </ Badge >
174- ) ) }
175- </ div >
176- ) }
177201 </ div >
178202 ) ;
179203} ;
0 commit comments