@@ -5,12 +5,11 @@ import { HelperText, Label } from './label';
5
5
export interface ChipData {
6
6
tag : string ;
7
7
image ?: string ;
8
+ alt ?: string ;
8
9
}
9
10
10
- export interface AutocompleteOption {
11
- text : string ;
11
+ export interface AutocompleteOption extends ChipData {
12
12
value ?: string ;
13
- image ?: string ;
14
13
}
15
14
16
15
export interface IChipsOptions {
@@ -66,13 +65,13 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
66
65
if ( Array . isArray ( data ) ) {
67
66
return data . map ( ( item ) => {
68
67
if ( typeof item === 'string' ) {
69
- return { text : item } ;
68
+ return { tag : item } ;
70
69
}
71
70
return item ;
72
71
} ) ;
73
72
}
74
73
return Object . entries ( data ) . map ( ( [ text , value ] ) => ( {
75
- text,
74
+ tag : text ,
76
75
value : value || text ,
77
76
} ) ) ;
78
77
} ;
@@ -93,7 +92,7 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
93
92
}
94
93
95
94
const allOptions = processAutocompleteData ( data ) ;
96
- const filtered = allOptions . filter ( ( option ) => option . text . toLowerCase ( ) . includes ( input ) ) ;
95
+ const filtered = allOptions . filter ( ( option ) => option . tag . toLowerCase ( ) . includes ( input ) ) ;
97
96
98
97
const limit = currentVnode . attrs . autocompleteOptions . limit || Infinity ;
99
98
state . autocompleteItems = filtered . slice ( 0 , limit ) ;
@@ -103,8 +102,9 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
103
102
104
103
const selectAutocompleteItem = ( item : AutocompleteOption ) => {
105
104
addChip ( {
106
- tag : item . text ,
105
+ tag : item . tag ,
107
106
image : item . image ,
107
+ alt : item . alt , // Preserve alt text when converting to chip
108
108
} ) ;
109
109
state . inputValue = '' ;
110
110
state . showAutocomplete = false ;
@@ -166,13 +166,21 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
166
166
state . selectedAutocompleteIndex + 1 ,
167
167
state . autocompleteItems . length - 1
168
168
) ;
169
+ const selectedItem = currentVnode ?. dom . querySelector ( '.autocomplete-item.selected' ) as HTMLElement ;
170
+ if ( selectedItem ) {
171
+ selectedItem . scrollIntoView ( { block : 'nearest' } ) ;
172
+ }
169
173
m . redraw ( ) ;
170
174
return ;
171
175
}
172
176
173
177
if ( e . key === 'ArrowUp' ) {
174
178
e . preventDefault ( ) ;
175
179
state . selectedAutocompleteIndex = Math . max ( state . selectedAutocompleteIndex - 1 , - 1 ) ;
180
+ const selectedItem = currentVnode ?. dom . querySelector ( '.autocomplete-item.selected' ) as HTMLElement ;
181
+ if ( selectedItem ) {
182
+ selectedItem . scrollIntoView ( { block : 'nearest' } ) ;
183
+ }
176
184
m . redraw ( ) ;
177
185
return ;
178
186
}
@@ -187,7 +195,11 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
187
195
if ( e . key === 'Enter' && target . value . trim ( ) ) {
188
196
e . preventDefault ( ) ;
189
197
addChip ( { tag : target . value . trim ( ) } ) ;
190
- } else if ( ( e . key === 'Backspace' || e . key === 'ArrowLeft' ) && ! target . value && state . chipsData . length ) {
198
+ } else if ( e . key === 'Backspace' && ! target . value && state . chipsData . length > 0 ) {
199
+ e . preventDefault ( ) ;
200
+ // Delete the last chip immediately when backspace is pressed in an empty input
201
+ deleteChip ( state . chipsData . length - 1 ) ;
202
+ } else if ( e . key === 'ArrowLeft' && ! target . value && state . chipsData . length ) {
191
203
e . preventDefault ( ) ;
192
204
selectChip ( state . chipsData . length - 1 ) ;
193
205
}
@@ -220,20 +232,11 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
220
232
currentVnode = vnode ;
221
233
} ,
222
234
223
- // onupdate: (vnode) => {
224
- // currentVnode = vnode;
225
- // const { data } = vnode.attrs;
226
- // if (data && JSON.stringify(data) !== JSON.stringify(state.chipsData)) {
227
- // state.chipsData = data;
228
- // }
229
- // },
230
-
231
235
onremove : ( ) => {
232
236
currentVnode = null ;
233
237
} ,
234
238
235
239
view : ( { attrs } ) => {
236
- // console.log(state);
237
240
const {
238
241
id,
239
242
required,
@@ -255,7 +258,7 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
255
258
return '' ;
256
259
} ;
257
260
258
- return m ( 'div .input-field' , { id, className } , [
261
+ return m ( '.input-field' , { id, className } , [
259
262
m (
260
263
'.chips.chips-initial' ,
261
264
{
@@ -276,7 +279,7 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
276
279
chip . image &&
277
280
m ( 'img' , {
278
281
src : chip . image ,
279
- alt : chip . tag ,
282
+ alt : chip . alt || chip . tag ,
280
283
} ) ,
281
284
chip . tag ,
282
285
m (
@@ -311,9 +314,10 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
311
314
onblur : ( ) => {
312
315
state . focused = false ;
313
316
setTimeout ( ( ) => {
317
+ state . showAutocomplete = false ;
314
318
state . selectedChip = null ;
315
319
m . redraw ( ) ;
316
- } , 100 ) ;
320
+ } , 150 ) ;
317
321
} ,
318
322
onkeydown : handleKeydown ,
319
323
} ) ,
@@ -326,31 +330,50 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
326
330
display : 'block' ,
327
331
opacity : 1 ,
328
332
transform : 'scaleX(1) scaleY(1)' ,
329
- width : '120px' ,
330
- left : '0px' ,
331
- top : '45px' ,
332
- height : '50px' ,
333
- transformOrigin : '0px 0px' ,
333
+ position : 'absolute' ,
334
+ width : '100%' ,
335
+ left : 0 ,
336
+ top : '100%' ,
337
+ maxHeight : '200px' ,
338
+ overflow : 'auto' ,
339
+ zIndex : 1000 ,
340
+ backgroundColor : '#fff' ,
341
+ boxShadow :
342
+ '0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 1px -2px rgba(0,0,0,0.12), 0 1px 5px 0 rgba(0,0,0,0.2)' ,
334
343
} ,
335
344
} ,
336
345
state . autocompleteItems . map ( ( item , index ) =>
337
346
m (
338
347
'li.autocomplete-item' ,
339
348
{
340
- key : item . text ,
349
+ key : item . tag ,
341
350
class : state . selectedAutocompleteIndex === index ? 'selected' : '' ,
351
+ style : {
352
+ padding : '12px 16px' ,
353
+ cursor : 'pointer' ,
354
+ backgroundColor : state . selectedAutocompleteIndex === index ? '#eee' : 'transparent' ,
355
+ } ,
342
356
onmousedown : ( e : MouseEvent ) => {
343
357
e . preventDefault ( ) ;
344
358
selectAutocompleteItem ( item ) ;
345
359
} ,
360
+ onmouseover : ( ) => {
361
+ state . selectedAutocompleteIndex = index ;
362
+ } ,
346
363
} ,
347
364
[
348
365
item . image &&
349
366
m ( 'img.autocomplete-item-image' , {
350
367
src : item . image ,
351
- alt : item . text ,
368
+ alt : item . alt || item . tag ,
369
+ style : {
370
+ width : '24px' ,
371
+ height : '24px' ,
372
+ marginRight : '8px' ,
373
+ verticalAlign : 'middle' ,
374
+ } ,
352
375
} ) ,
353
- m ( 'span.autocomplete-item-text' , item . text ) ,
376
+ m ( 'span.autocomplete-item-text' , item . tag ) ,
354
377
]
355
378
)
356
379
)
0 commit comments