This commit is contained in:
2026-03-31 19:15:18 +02:00
parent d62e99b17e
commit 0bdbd70ab3
9 changed files with 2987 additions and 0 deletions

View File

@@ -0,0 +1,376 @@
# Autocomplete
Searchable dropdown with autocomplete functionality, keyboard navigation, and reactive options.
## Tag
`Autocomplete`
## Props
| Prop | Type | Default | Description |
| :----------- | :------------------------------------------------ | :------------------ | :----------------------------------------------- |
| `label` | `string` | `-` | Label text for the input |
| `options` | `Array<string \| {value: string, label: string}>` | `[]` | Options to search from |
| `value` | `string \| Signal<string>` | `''` | Selected value |
| `placeholder`| `string` | `'Search...'` | Placeholder text |
| `onSelect` | `function` | `-` | Called when an option is selected |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
## Live Examples
### Basic Autocomplete
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
const BasicDemo = () => {
const selected = $('');
const fruits = ['Apple', 'Banana', 'Orange', 'Grape', 'Strawberry', 'Mango', 'Pineapple', 'Watermelon'];
return Autocomplete({
label: 'Search fruit',
options: fruits,
value: selected,
onSelect: (value) => selected(value)
});
};
$mount(BasicDemo, '#demo-basic');
```
### With Objects
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-objects" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
</div>
</div>
```javascript
const ObjectsDemo = () => {
const selected = $('');
const selectedLabel = $('');
const countries = [
{ value: 'mx', label: 'Mexico' },
{ value: 'us', label: 'United States' },
{ value: 'ca', label: 'Canada' },
{ value: 'br', label: 'Brazil' },
{ value: 'ar', label: 'Argentina' },
{ value: 'es', label: 'Spain' }
];
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Autocomplete({
label: 'Search country',
options: countries,
value: selectedLabel,
onSelect: (item) => {
const selectedItem = typeof item === 'string'
? countries.find(c => c.label === item)
: item;
selected(selectedItem?.value || '');
selectedLabel(selectedItem?.label || '');
}
}),
Div({ class: 'alert alert-success' }, [
`Selected: ${selected()} - ${selectedLabel()}`
])
]);
};
$mount(ObjectsDemo, '#demo-objects');
```
### With Reactive Display
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-reactive" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
const ReactiveDemo = () => {
const selected = $('');
const programmingLanguages = [
'JavaScript', 'Python', 'Java', 'C++', 'Ruby', 'Go', 'Rust', 'TypeScript', 'Swift', 'Kotlin'
];
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Autocomplete({
label: 'Programming language',
options: programmingLanguages,
value: selected,
onSelect: (value) => selected(value)
}),
() => selected() ? Div({ class: 'alert alert-info' }, [
`You selected: ${selected()}`
]) : null
]);
};
$mount(ReactiveDemo, '#demo-reactive');
```
### Dynamic Options
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-dynamic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
const DynamicDemo = () => {
const selected = $('');
const filterType = $('all');
const allItems = {
fruits: ['Apple', 'Banana', 'Orange', 'Mango'],
vegetables: ['Carrot', 'Broccoli', 'Spinach', 'Potato'],
all: ['Apple', 'Banana', 'Orange', 'Mango', 'Carrot', 'Broccoli', 'Spinach', 'Potato']
};
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Select({
label: 'Category',
options: [
{ value: 'all', label: 'All items' },
{ value: 'fruits', label: 'Fruits' },
{ value: 'vegetables', label: 'Vegetables' }
],
value: filterType,
onchange: (e) => filterType(e.target.value)
}),
Autocomplete({
label: 'Search item',
options: () => allItems[filterType()],
value: selected,
onSelect: (value) => selected(value)
})
]);
};
$mount(DynamicDemo, '#demo-dynamic');
```
### All Variants
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-variants" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
</div>
</div>
```javascript
const VariantsDemo = () => {
const colors = ['Red', 'Blue', 'Green', 'Yellow', 'Purple', 'Orange', 'Pink', 'Brown', 'Black', 'White'];
return Div({ class: 'flex flex-col gap-4' }, [
Div({}, [
Autocomplete({
label: 'Primary style',
class: 'input-primary',
options: colors,
value: $(''),
placeholder: 'Search colors...'
})
]),
Div({}, [
Autocomplete({
label: 'Secondary style',
class: 'input-secondary',
options: colors,
value: $(''),
placeholder: 'Search colors...'
})
]),
Div({}, [
Autocomplete({
label: 'Ghost style',
class: 'input-ghost',
options: colors,
value: $(''),
placeholder: 'Search colors...'
})
])
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initAutocompleteExamples = () => {
// 1. Basic Autocomplete
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const selected = $('');
const fruits = ['Apple', 'Banana', 'Orange', 'Grape', 'Strawberry', 'Mango', 'Pineapple', 'Watermelon'];
return Autocomplete({
label: 'Search fruit',
options: fruits,
value: selected,
onSelect: (value) => selected(value)
});
};
$mount(BasicDemo, basicTarget);
}
// 2. With Objects
const objectsTarget = document.querySelector('#demo-objects');
if (objectsTarget && !objectsTarget.hasChildNodes()) {
const ObjectsDemo = () => {
const selected = $('');
const selectedLabel = $('');
const countries = [
{ value: 'mx', label: 'Mexico' },
{ value: 'us', label: 'United States' },
{ value: 'ca', label: 'Canada' },
{ value: 'br', label: 'Brazil' },
{ value: 'ar', label: 'Argentina' },
{ value: 'es', label: 'Spain' }
];
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Autocomplete({
label: 'Search country',
options: countries,
value: selectedLabel,
onSelect: (item) => {
const selectedItem = typeof item === 'string'
? countries.find(c => c.label === item)
: item;
selected(selectedItem?.value || '');
selectedLabel(selectedItem?.label || '');
}
}),
Div({ class: 'alert alert-success' }, [
`Selected: ${selected()} - ${selectedLabel()}`
])
]);
};
$mount(ObjectsDemo, objectsTarget);
}
// 3. Reactive Display
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const selected = $('');
const programmingLanguages = [
'JavaScript', 'Python', 'Java', 'C++', 'Ruby', 'Go', 'Rust', 'TypeScript', 'Swift', 'Kotlin'
];
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Autocomplete({
label: 'Programming language',
options: programmingLanguages,
value: selected,
onSelect: (value) => selected(value)
}),
() => selected() ? Div({ class: 'alert alert-info' }, [
`You selected: ${selected()}`
]) : null
]);
};
$mount(ReactiveDemo, reactiveTarget);
}
// 4. Dynamic Options
const dynamicTarget = document.querySelector('#demo-dynamic');
if (dynamicTarget && !dynamicTarget.hasChildNodes()) {
const DynamicDemo = () => {
const selected = $('');
const filterType = $('all');
const allItems = {
fruits: ['Apple', 'Banana', 'Orange', 'Mango'],
vegetables: ['Carrot', 'Broccoli', 'Spinach', 'Potato'],
all: ['Apple', 'Banana', 'Orange', 'Mango', 'Carrot', 'Broccoli', 'Spinach', 'Potato']
};
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Select({
label: 'Category',
options: [
{ value: 'all', label: 'All items' },
{ value: 'fruits', label: 'Fruits' },
{ value: 'vegetables', label: 'Vegetables' }
],
value: filterType,
onchange: (e) => filterType(e.target.value)
}),
Autocomplete({
label: 'Search item',
options: () => allItems[filterType()],
value: selected,
onSelect: (value) => selected(value)
})
]);
};
$mount(DynamicDemo, dynamicTarget);
}
// 5. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
const colors = ['Red', 'Blue', 'Green', 'Yellow', 'Purple', 'Orange', 'Pink', 'Brown', 'Black', 'White'];
return Div({ class: 'flex flex-col gap-4' }, [
Div({}, [
Autocomplete({
label: 'Primary style',
class: 'input-primary',
options: colors,
value: $(''),
placeholder: 'Search colors...'
})
]),
Div({}, [
Autocomplete({
label: 'Secondary style',
class: 'input-secondary',
options: colors,
value: $(''),
placeholder: 'Search colors...'
})
]),
Div({}, [
Autocomplete({
label: 'Ghost style',
class: 'input-ghost',
options: colors,
value: $(''),
placeholder: 'Search colors...'
})
])
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initAutocompleteExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initAutocompleteExamples);
});
}
})();
</script>