DocsComposantsCombobox
Combobox
A select you can search, with keyboard navigation; its value is posted with the form.
Alpine.js2 fichiers
<pb-ui.combobox name="framework" placeholder="Choisir un framework…" search_placeholder="Chercher…" empty="Aucun framework." class="w-60">
<pb-ui.combobox-item value="django">Django</pb-ui.combobox-item>
<pb-ui.combobox-item value="flask">Flask</pb-ui.combobox-item>
<pb-ui.combobox-item value="fastapi">FastAPI</pb-ui.combobox-item>
<pb-ui.combobox-item value="litestar">Litestar</pb-ui.combobox-item>
<pb-ui.combobox-item value="starlette">Starlette</pb-ui.combobox-item>
</pb-ui.combobox>Installation
pyblade-ui add combobox- Installez les dépendances
Alpine.js, chargé dans votre
<head>. - Copiez ces fichiers dans votre projetcomponents/ui/combobox.html
@props({'name': '', 'value': '', 'placeholder': 'Select an option...', 'search_placeholder': 'Search...', 'empty': 'No results found.'}) <div data-slot="combobox" x-data="{ open: false, search: '', value: '{{ value }}', label: '', match(text) { return text.toLowerCase().includes(this.search.trim().toLowerCase()) }, all() { return [...this.$refs.list.querySelectorAll('[data-slot=combobox-item]')] }, found() { return this.all().filter(i => this.match(i.textContent)) }, pick(v, l) { this.value = this.value === v ? '' : v; this.label = this.value ? l : ''; this.open = false; this.search = ''; this.$nextTick(() => this.$refs.trigger.focus()); this.$dispatch('change', this.value) }, move(step) { const items = this.found(), at = items.indexOf(document.activeElement); const next = items[at + step] || items[step > 0 ? 0 : items.length - 1]; if (next) next.focus(); }, }" x-init="$nextTick(() => { const el = all().find(i => i.dataset.value === value); if (el) label = el.textContent.trim() })" x-on:keydown.escape="open = false" x-on:keydown.down.prevent="open ? move(1) : (open = true)" x-on:keydown.up.prevent="move(-1)" x-on:click.outside="open = false" {{ attributes.cn("relative w-full") }}> <input type="hidden" name="{{ name }}" x-bind:value="value" /> <button type="button" role="combobox" x-ref="trigger" x-bind:aria-expanded="open" x-on:click="open = !open; open && $nextTick(() => $refs.search.focus())" class="flex h-9 w-full cursor-pointer items-center gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-left text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30 dark:hover:bg-input/50"> <span class="truncate" x-bind:class="{ 'text-muted-foreground': !label }" x-text="label || '{{ placeholder }}'">{{ placeholder }}</span> <svg class="ml-auto size-4 shrink-0 opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg> </button> <div data-slot="combobox-content" x-show="open" x-cloak x-transition:enter="duration-100 ease-out" x-transition:enter-start="scale-95 opacity-0" x-transition:enter-end="scale-100 opacity-100" class="absolute top-full left-0 z-50 mt-1 w-full min-w-[12rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md"> <div class="flex h-9 items-center gap-2 border-b px-3"> <svg class="size-4 shrink-0 opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg> <input type="text" x-ref="search" x-model="search" autocomplete="off" placeholder="{{ search_placeholder }}" x-on:keydown.enter.prevent="found()[0] && found()[0].click()" class="flex h-9 w-full bg-transparent text-sm outline-hidden placeholder:text-muted-foreground" /> </div> <div role="listbox" x-ref="list" class="max-h-60 overflow-y-auto p-1"> {{ slot }} <div x-show="search && !found().length" class="py-6 text-center text-sm">{{ empty }}</div> </div> </div> </div>components/ui/combobox_item.html@props({'value': ''}) <button type="button" role="option" data-slot="combobox-item" data-value="{{ value }}" x-show="match($el.textContent)" x-bind:aria-selected="value === '{{ value }}'" x-on:click="pick('{{ value }}', $el.textContent.trim())" {{ attributes.cn( "relative flex w-full cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-left text-sm outline-hidden select-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", ) }}>{{ slot }}<span class="absolute right-2 flex size-3.5 items-center justify-center" x-show="value === '{{ value }}'"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></span></button> - Adaptez le chemin à votre projet
Le dossier
componentsest celui déclaré danspyblade.toml.
Utilisation
Les balises <pb-ui.combobox> <pb-ui.combobox-item> sont disponibles dans tous vos templates.
<pb-ui.combobox name="framework" placeholder="Select a framework..." class="w-56">
<pb-ui.combobox-item value="django">Django</pb-ui.combobox-item>
<pb-ui.combobox-item value="flask">Flask</pb-ui.combobox-item>
<pb-ui.combobox-item value="fastapi">FastAPI</pb-ui.combobox-item>
</pb-ui.combobox>Exemples
Avec une valeur et un libellé
<div class="grid w-64 gap-2">
<pb-ui.label>Langue</pb-ui.label>
<pb-ui.combobox name="language" value="fr" search_placeholder="Chercher une langue…">
<pb-ui.combobox-item value="en">English</pb-ui.combobox-item>
<pb-ui.combobox-item value="fr">Français</pb-ui.combobox-item>
<pb-ui.combobox-item value="es">Español</pb-ui.combobox-item>
<pb-ui.combobox-item value="sw">Kiswahili</pb-ui.combobox-item>
<pb-ui.combobox-item value="ln">Lingála</pb-ui.combobox-item>
</pb-ui.combobox>
</div>Props
<pb-ui.combobox>
| Prop | Défaut |
|---|---|
| name | '' |
| value | '' |
| placeholder | 'Select an option...' |
| search_placeholder | 'Search...' |
| empty | 'No results found.' |
<pb-ui.combobox-item>
| Prop | Défaut |
|---|---|
| value | '' |
Tout autre attribut (class, id, x-on:click, hx-post…) est transmis à l'élément racine ; vos classes Tailwind l'emportent sur celles du composant.