I made a client sided ComboBox with Vaadin Icons as value.
Feel free to use or give some feedback
import "@vaadin/combo-box";
import "@vaadin/icon";
import "@vaadin/icons";
import '@vaadin/item';
import '@vaadin/list-box';
import '@vaadin/multi-select-combo-box';
import "@vaadin/tooltip";
import { ComboBoxFilterChangedEvent, ComboBoxValueChangedEvent } from "@vaadin/combo-box";
import type { ComboBoxLitRenderer } from '@vaadin/combo-box/lit.js';
import { comboBoxRenderer } from '@vaadin/combo-box/lit.js';
import { SelectValueChangedEvent } from "@vaadin/select";
import { html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { View } from "../view";
import fontIcons from '@vaadin/icons/assets/vaadin-font-icons.json' assert { type: 'json' };
// import { Iconset } from '@vaadin/icon/vaadin-iconset.js';
declare type FontIconType = {
name: string
code: string
meta: string[]
categories: string[]
}
@customElement("icon-combo-box")
export class IconComboBox extends View {
@state()
private items: FontIconType[] = [];
@state()
private filteredItems: FontIconType[] = [];
@property({ type: String })
value?: string;
@property({ type: Boolean })
disabled: boolean = false
@property({ type: Boolean })
readonly: boolean = false
connectedCallback() {
super.connectedCallback();
this.loadFiles();
}
private loadFiles() {
this.items = fontIcons;
this.items.sort((i1, i2) => i1.name.localeCompare(i2.name));
this.filteredItems = this.items
// optionally get only names
// const iconset: any = Iconset.getIconset('vaadin');
// const vaadinIcons = iconset._icons;
// this.icons = Array.from(new Map(Object.entries(vaadinIcons)).keys()).filter((key): key is string => typeof key === 'string');
}
private filterChanged(event: ComboBoxFilterChangedEvent) {
const filter = event.detail.value;
this.filteredItems = this.items.filter(({ name, meta, categories }) =>
name.toLowerCase().startsWith(filter.toLowerCase()) ||
meta.some(m => m.toLowerCase().startsWith(filter.toLowerCase())) ||
categories.some(c => c.toLowerCase().startsWith(filter.toLowerCase()))
);
}
private valueChanged(event: ComboBoxValueChangedEvent) {
this.value = event.detail.value
}
render() {
return html`
<vaadin-combo-box
placeholder="Select an icon"
.filteredItems="${this.filteredItems}"
@value-changed="${this.valueChanged}"
@filter-changed="${this.filterChanged}"
item-value-path="name"
item-label-path="name"
clear-button-visible
@selected-items-changed=${(e: SelectValueChangedEvent) => { }}
?readonly=${this.readonly || this.disabled}
${comboBoxRenderer(this.itemRenderer(), [])}
>
</vaadin-combo-box>
`
}
private itemRenderer(): ComboBoxLitRenderer<FontIconType> {
return icon => html`
<div id=${'icon-item-' + icon.name} class="flex flex-row gap-m items-center" title=${this.generateItemDescription(icon)}>
<vaadin-icon
style="height: var(--lumo-space-m); width: var(--lumo-space-m);"
icon=${'vaadin:' + icon.name}>
</vaadin-icon>
<span class="text-s">
${icon.name}
</span>
</div>
`;
}
generateItemDescription(icon: FontIconType): string {
if (!icon || (!icon.categories && !icon.meta)) {
return ''
}
return 'Categorie: ' + icon.categories.join(' ') + ' - Tags: ' + icon.meta.join(' ')
}
}