This commit is contained in:
@@ -1,276 +1,60 @@
|
||||
# Autocomplete
|
||||
|
||||
Searchable dropdown with autocomplete functionality, keyboard navigation, and reactive items.
|
||||
|
||||
## Tag
|
||||
|
||||
`Autocomplete`
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
| :------------ | :---------------------------------------------------------- | :------------ | :--------------------------------------- |
|
||||
| `class` | `string` | `''` | Additional CSS classes for the container |
|
||||
| `items` | `Array<string \| {value: string, label: string}> \| Signal` | `[]` | Items to search from |
|
||||
| `value` | `string \| Signal<string>` | `''` | Selected value (reactive) |
|
||||
| `onselect` | `function(item)` | `-` | Called when an option is selected |
|
||||
| `label` | `string` | `-` | Label text for the input |
|
||||
| `placeholder` | `string` | `'Buscar...'` | Placeholder text |
|
||||
|
||||
## Styling
|
||||
|
||||
Autocomplete wraps a **daisyUI Input component** internally. All Input styling classes work:
|
||||
|
||||
| Category | Keywords | Description |
|
||||
| :------- | :------------------------------------------------------------------------------------------------------------------------------- | :-------------------- |
|
||||
| Color | `input-primary`, `input-secondary`, `input-accent`, `input-ghost`, `input-info`, `input-success`, `input-warning`, `input-error` | Input color variants |
|
||||
| Size | `input-xs`, `input-sm`, `input-md`, `input-lg` | Input scale |
|
||||
| Style | `input-bordered` (default), `input-ghost` | Visual style variants |
|
||||
|
||||
> For further details, check the [daisyUI Input Documentation](https://daisyui.com/components/input) – Full reference for CSS classes.
|
||||
|
||||
## 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"></div>
|
||||
<div id="app-demo" class="bg-base-100 rounded-xl border border-base-300"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```js
|
||||
const { Autocomplete, mount } = window;
|
||||
const paises = [
|
||||
"España",
|
||||
"México",
|
||||
"Argentina",
|
||||
"Colombia",
|
||||
"Chile",
|
||||
"Perú",
|
||||
"Venezuela",
|
||||
{ label: "Estados Unidos", value: "US" },
|
||||
{ label: "Canadá", value: "CA" },
|
||||
{ label: "Reino Unido", value: "UK" },
|
||||
];
|
||||
|
||||
const BasicDemo = () => {
|
||||
const selected = $("");
|
||||
const fruits = [
|
||||
"Apple",
|
||||
"Banana",
|
||||
"Orange",
|
||||
"Grape",
|
||||
"Strawberry",
|
||||
"Mango",
|
||||
"Pineapple",
|
||||
"Watermelon",
|
||||
const App = () => {
|
||||
const password = $("");
|
||||
|
||||
// Lógica de validación sencilla
|
||||
const requirements = [
|
||||
{ label: "Mínimo 8 caracteres", check: () => password().length >= 8 },
|
||||
{ label: "Incluye un número", check: () => /\d/.test(password()) },
|
||||
{ label: "Símbolo especial", check: () => /[^A-Za-z0-9]/.test(password()) }
|
||||
];
|
||||
|
||||
return Autocomplete({
|
||||
items: 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"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```js
|
||||
const { Autocomplete, Div, mount } = window;
|
||||
|
||||
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({
|
||||
items: 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-info mt-4" },
|
||||
() => `Selected: ${selected()} - ${selectedLabel()}`,
|
||||
),
|
||||
return div({ class: "p-10 max-w-md" }, [
|
||||
Input({
|
||||
type: "password",
|
||||
value: password,
|
||||
class: "input-warning",
|
||||
oninput: (e) => password(e.target.value),
|
||||
label: "Pass",
|
||||
float: true,
|
||||
left: span({ class: "icon-[lucide--lock] mr-2" }),
|
||||
// El contenido que se animará con fx() dentro del Input
|
||||
content: div({ class: "mt-2 p-4 bg-base-200 rounded-lg shadow-xl border border-base-300" }, [
|
||||
span({ class: "text-xs font-bold uppercase opacity-50" }, "Seguridad:"),
|
||||
ul({ class: "mt-2 space-y-1" },
|
||||
requirements.map(req => h('li', {
|
||||
class: () => `flex items-center text-sm ${req.check() ? 'text-success' : 'text-error opacity-50'}`
|
||||
}, [
|
||||
span({ class: () => `mr-2 ${req.check() ? 'icon-[lucide--check]' : 'icon-[lucide--x]'}` }),
|
||||
req.label
|
||||
]))
|
||||
)
|
||||
])
|
||||
})
|
||||
]);
|
||||
};
|
||||
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"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```js
|
||||
const { Autocomplete, Div, mount } = window;
|
||||
|
||||
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({
|
||||
items: programmingLanguages,
|
||||
value: selected,
|
||||
onselect: (value) => selected(value),
|
||||
}),
|
||||
() =>
|
||||
selected()
|
||||
? Div(
|
||||
{ class: "alert alert-success mt-4" },
|
||||
`You selected: ${selected()}`,
|
||||
)
|
||||
: null,
|
||||
]);
|
||||
};
|
||||
mount(ReactiveDemo, "#demo-reactive");
|
||||
```
|
||||
|
||||
### Dynamic Items
|
||||
|
||||
<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"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```js
|
||||
const { Autocomplete, Select, SelectItems, Div, mount } = window;
|
||||
|
||||
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",
|
||||
],
|
||||
};
|
||||
|
||||
const options = [
|
||||
{ value: "all", label: "All items" },
|
||||
{ value: "fruits", label: "Fruits" },
|
||||
{ value: "vegetables", label: "Vegetables" },
|
||||
];
|
||||
|
||||
const handleFilterChange = (e) => {
|
||||
filterType(e.target.value);
|
||||
selected("");
|
||||
setTimeout(() => selected(""), 300);
|
||||
};
|
||||
|
||||
return Div({ class: "flex flex-col gap-4 w-full" }, [
|
||||
Select(
|
||||
{
|
||||
class: "select select-bordered w-full",
|
||||
value: filterType,
|
||||
onchange: handleFilterChange,
|
||||
},
|
||||
SelectItems({ items: options }),
|
||||
),
|
||||
Div({ key: () => filterType() }, [
|
||||
Autocomplete({
|
||||
items: () => 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"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```js
|
||||
const { Autocomplete, Div, mount } = window;
|
||||
|
||||
const VariantsDemo = () => {
|
||||
const colors = [
|
||||
"Red",
|
||||
"Blue",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Purple",
|
||||
"Orange",
|
||||
"Pink",
|
||||
"Brown",
|
||||
"Black",
|
||||
"White",
|
||||
];
|
||||
|
||||
return Div({ class: "flex flex-col gap-4" }, [
|
||||
Div({ class: "font-bold" }, "Primary"),
|
||||
Autocomplete({
|
||||
class: "input-primary",
|
||||
items: colors,
|
||||
value: $(""),
|
||||
placeholder: "Search colors...",
|
||||
}),
|
||||
Div({ class: "font-bold mt-2" }, "Secondary"),
|
||||
Autocomplete({
|
||||
class: "input-secondary",
|
||||
items: colors,
|
||||
value: $(""),
|
||||
placeholder: "Search colors...",
|
||||
}),
|
||||
Div({ class: "font-bold mt-2" }, "Ghost"),
|
||||
Autocomplete({
|
||||
class: "input-ghost",
|
||||
items: colors,
|
||||
value: $(""),
|
||||
placeholder: "Search colors...",
|
||||
}),
|
||||
]);
|
||||
};
|
||||
mount(VariantsDemo, "#demo-variants");
|
||||
|
||||
mount(App, "#app-demo");
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user