Skip to content

Commit 62b42b6

Browse files
Implement arrow down in chips autocomplete
1 parent 4b82f74 commit 62b42b6

File tree

2 files changed

+52
-29
lines changed

2 files changed

+52
-29
lines changed

packages/lib/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@
4747
"typedoc": "^0.27.6",
4848
"typescript": "^5.7.3"
4949
}
50-
}
50+
}

packages/lib/src/chip.ts

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@ import { HelperText, Label } from './label';
55
export interface ChipData {
66
tag: string;
77
image?: string;
8+
alt?: string;
89
}
910

10-
export interface AutocompleteOption {
11-
text: string;
11+
export interface AutocompleteOption extends ChipData {
1212
value?: string;
13-
image?: string;
1413
}
1514

1615
export interface IChipsOptions {
@@ -66,13 +65,13 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
6665
if (Array.isArray(data)) {
6766
return data.map((item) => {
6867
if (typeof item === 'string') {
69-
return { text: item };
68+
return { tag: item };
7069
}
7170
return item;
7271
});
7372
}
7473
return Object.entries(data).map(([text, value]) => ({
75-
text,
74+
tag: text,
7675
value: value || text,
7776
}));
7877
};
@@ -93,7 +92,7 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
9392
}
9493

9594
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));
9796

9897
const limit = currentVnode.attrs.autocompleteOptions.limit || Infinity;
9998
state.autocompleteItems = filtered.slice(0, limit);
@@ -103,8 +102,9 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
103102

104103
const selectAutocompleteItem = (item: AutocompleteOption) => {
105104
addChip({
106-
tag: item.text,
105+
tag: item.tag,
107106
image: item.image,
107+
alt: item.alt, // Preserve alt text when converting to chip
108108
});
109109
state.inputValue = '';
110110
state.showAutocomplete = false;
@@ -166,13 +166,21 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
166166
state.selectedAutocompleteIndex + 1,
167167
state.autocompleteItems.length - 1
168168
);
169+
const selectedItem = currentVnode?.dom.querySelector('.autocomplete-item.selected') as HTMLElement;
170+
if (selectedItem) {
171+
selectedItem.scrollIntoView({ block: 'nearest' });
172+
}
169173
m.redraw();
170174
return;
171175
}
172176

173177
if (e.key === 'ArrowUp') {
174178
e.preventDefault();
175179
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+
}
176184
m.redraw();
177185
return;
178186
}
@@ -187,7 +195,11 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
187195
if (e.key === 'Enter' && target.value.trim()) {
188196
e.preventDefault();
189197
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) {
191203
e.preventDefault();
192204
selectChip(state.chipsData.length - 1);
193205
}
@@ -220,20 +232,11 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
220232
currentVnode = vnode;
221233
},
222234

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-
231235
onremove: () => {
232236
currentVnode = null;
233237
},
234238

235239
view: ({ attrs }) => {
236-
// console.log(state);
237240
const {
238241
id,
239242
required,
@@ -255,7 +258,7 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
255258
return '';
256259
};
257260

258-
return m('div.input-field', { id, className }, [
261+
return m('.input-field', { id, className }, [
259262
m(
260263
'.chips.chips-initial',
261264
{
@@ -276,7 +279,7 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
276279
chip.image &&
277280
m('img', {
278281
src: chip.image,
279-
alt: chip.tag,
282+
alt: chip.alt || chip.tag,
280283
}),
281284
chip.tag,
282285
m(
@@ -311,9 +314,10 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
311314
onblur: () => {
312315
state.focused = false;
313316
setTimeout(() => {
317+
state.showAutocomplete = false;
314318
state.selectedChip = null;
315319
m.redraw();
316-
}, 100);
320+
}, 150);
317321
},
318322
onkeydown: handleKeydown,
319323
}),
@@ -326,31 +330,50 @@ export const Chips: m.FactoryComponent<IChipsOptions> = () => {
326330
display: 'block',
327331
opacity: 1,
328332
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)',
334343
},
335344
},
336345
state.autocompleteItems.map((item, index) =>
337346
m(
338347
'li.autocomplete-item',
339348
{
340-
key: item.text,
349+
key: item.tag,
341350
class: state.selectedAutocompleteIndex === index ? 'selected' : '',
351+
style: {
352+
padding: '12px 16px',
353+
cursor: 'pointer',
354+
backgroundColor: state.selectedAutocompleteIndex === index ? '#eee' : 'transparent',
355+
},
342356
onmousedown: (e: MouseEvent) => {
343357
e.preventDefault();
344358
selectAutocompleteItem(item);
345359
},
360+
onmouseover: () => {
361+
state.selectedAutocompleteIndex = index;
362+
},
346363
},
347364
[
348365
item.image &&
349366
m('img.autocomplete-item-image', {
350367
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+
},
352375
}),
353-
m('span.autocomplete-item-text', item.text),
376+
m('span.autocomplete-item-text', item.tag),
354377
]
355378
)
356379
)

0 commit comments

Comments
 (0)