A component that allows the user to select one item from a list of options that can be opened and closed.
import { Component } from '@angular/core';
import {
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
} from '@favian/headwind-ui';
import { animate, state, style, transition, trigger } from '@angular/animations';
@Component({
selector: 'app-basic-select-example',
standalone: true,
imports: [
HeadwindSelectComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindOptionComponent,
],
template: `
<headwind-select
#select
[(value)]="value"
[class]="select.opened ? 'border-rose-500 outline outline-1 outline-rose-500' : 'border-zinc-300'"
class="inline-flex h-10 w-full max-w-[200px] cursor-pointer select-none items-center justify-between rounded-lg border bg-white px-3 focus:border-rose-500 focus:outline focus:outline-1 focus:outline-rose-500"
>
<div [class.text-zinc-400]="!value" class="text-sm">
{{ value ? value : 'Select Option' }}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
<ng-template headwindOptionsOverlay>
<headwind-options-overlay [@fadeIn] class="block max-h-[200px] overflow-auto rounded-md bg-white p-2 shadow-xl">
@for (option of options; track option) {
<headwind-option
#headwindOption
[class.text-rose-500]="headwindOption.selected"
[class.bg-rose-50]="headwindOption.focused"
[value]="option"
class="block cursor-pointer select-none px-3 py-2 text-sm hover:bg-rose-50"
>
{{ option }}
</headwind-option>
}
</headwind-options-overlay>
</ng-template>
</headwind-select>
`,
styles: [
`
@tailwind components;
@layer components {
.headwind-options-overlay::-webkit-scrollbar-track {
@apply my-2;
}
}
`,
],
animations: [
trigger('fadeIn', [
state(
'void',
style({
opacity: 0,
}),
),
transition(
'void => *',
animate(
'.1s',
style({
opacity: 1,
}),
),
),
transition('* => void', animate('.1s')),
]),
],
})
export class BasicSelectExampleComponent {
value = '';
options = [
'Tori Day',
'Ishaan Martinez',
'Alvin Bailey',
'Opal Morton',
'Evelynn Hobbs',
'Leila Pacheco',
'Fatima Shaw',
'Alexandria Huynh',
];
}
Each component used to implement Select has the same class name as its name. You can set styles using the class name as a selector in a style file.
import { Component } from '@angular/core';
import {
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
} from '@favian/headwind-ui';
@Component({
selector: 'app-select-classes-example',
standalone: true,
imports: [
HeadwindSelectComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindOptionComponent,
],
template: `
<headwind-select #select>
<div [class.text-zinc-400]="!select.value" class="text-sm">
{{ select.value ? select.value : 'Select Option' }}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
<ng-template headwindOptionsOverlay>
<headwind-options-overlay>
@for (option of options; track option) {
<headwind-option [value]="option">
{{ option }}
</headwind-option>
}
</headwind-options-overlay>
</ng-template>
</headwind-select>
`,
styles: [
`
@tailwind components;
@layer components {
.headwind-select {
@apply inline-flex h-10 w-full max-w-[200px] cursor-pointer select-none items-center justify-between rounded-lg border bg-white px-3;
}
.headwind-options-overlay {
@apply block max-h-[200px] overflow-auto rounded-md bg-white p-2 shadow-xl;
}
.headwind-option {
@apply block cursor-pointer select-none px-3 py-2 text-sm hover:bg-rose-50;
}
}
`,
],
})
export class SelectClassesExampleComponent {
options = [
'Tori Day',
'Ishaan Martinez',
'Alvin Bailey',
'Opal Morton',
'Evelynn Hobbs',
'Leila Pacheco',
'Fatima Shaw',
'Alexandria Huynh',
];
}
Each <headwind-option>
uses the internal property of the overlay component to give the effect of being focused. When <headwind-option>
is brought to the focused state by the overlay component, the .headwind-focused
class is added.
import { Component } from '@angular/core';
import {
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
} from '@favian/headwind-ui';
@Component({
selector: 'app-select-focused-class-example',
standalone: true,
imports: [
HeadwindSelectComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindOptionComponent,
],
template: `
<headwind-select #select>
<div [class.text-zinc-400]="!select.value" class="text-sm">
{{ select.value ? select.value : 'Select Option' }}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
<ng-template headwindOptionsOverlay>
<headwind-options-overlay>
@for (option of options; track option) {
<headwind-option [value]="option">
{{ option }}
</headwind-option>
}
</headwind-options-overlay>
</ng-template>
</headwind-select>
`,
styles: [
`
@tailwind components;
@layer components {
.headwind-select {
@apply inline-flex h-10 w-full max-w-[200px] cursor-pointer select-none items-center justify-between rounded-lg border bg-white px-3;
}
.headwind-options-overlay {
@apply block max-h-[200px] overflow-auto rounded-md bg-white p-2 shadow-xl;
}
.headwind-option {
@apply block cursor-pointer select-none px-3 py-2 text-sm hover:bg-rose-50;
&.headwind-focused {
@apply bg-rose-50;
}
}
}
`,
],
})
export class SelectFocusedClassExampleComponent {
options = [
'Tori Day',
'Ishaan Martinez',
'Alvin Bailey',
'Opal Morton',
'Evelynn Hobbs',
'Leila Pacheco',
'Fatima Shaw',
'Alexandria Huynh',
];
}
When a option is selected, the <headwind-option>
component has the class .headwind-selected
.
import { Component } from '@angular/core';
import {
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
} from '@favian/headwind-ui';
@Component({
selector: 'app-select-selected-class-example',
standalone: true,
imports: [
HeadwindSelectComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindOptionComponent,
],
template: `
<headwind-select #select>
<div [class.text-zinc-400]="!select.value" class="text-sm">
{{ select.value ? select.value : 'Select Option' }}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
<ng-template headwindOptionsOverlay>
<headwind-options-overlay>
@for (option of options; track option) {
<headwind-option [value]="option">
{{ option }}
</headwind-option>
}
</headwind-options-overlay>
</ng-template>
</headwind-select>
`,
styles: [
`
@tailwind components;
@layer components {
.headwind-select {
@apply inline-flex h-10 w-full max-w-[200px] cursor-pointer select-none items-center justify-between rounded-lg border bg-white px-3;
}
.headwind-options-overlay {
@apply block max-h-[200px] overflow-auto rounded-md bg-white p-2 shadow-xl;
}
.headwind-option {
@apply block cursor-pointer select-none px-3 py-2 text-sm hover:bg-rose-50;
&.headwind-selected {
@apply text-rose-500;
}
&.headwind-focused {
@apply bg-rose-50;
}
}
}
`,
],
})
export class SelectSelectedClassExampleComponent {
options = [
'Tori Day',
'Ishaan Martinez',
'Alvin Bailey',
'Opal Morton',
'Evelynn Hobbs',
'Leila Pacheco',
'Fatima Shaw',
'Alexandria Huynh',
];
}
Select can bind value to property using value
input and valueChange
output.
import { Component } from '@angular/core';
import {
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
} from '@favian/headwind-ui';
@Component({
selector: 'app-select-value-example',
standalone: true,
imports: [
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
],
template: `
<headwind-select
[(value)]="value"
class="inline-flex h-10 w-full max-w-[200px] cursor-pointer select-none items-center justify-between rounded-lg border bg-white px-3"
>
<div [class.text-zinc-400]="!value" class="text-sm">
{{ value ? value : 'Select Option' }}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
<ng-template headwindOptionsOverlay>
<headwind-options-overlay class="block max-h-[200px] overflow-auto rounded-md bg-white p-2 shadow-xl">
@for (option of options; track option) {
<headwind-option
#headwindOption
[class.text-rose-500]="headwindOption.selected"
[class.bg-rose-50]="headwindOption.focused"
[value]="option"
class="block cursor-pointer select-none px-3 py-2 text-sm hover:bg-rose-50"
>
{{ option }}
</headwind-option>
}
</headwind-options-overlay>
</ng-template>
</headwind-select>
`,
})
export class SelectValueExampleComponent {
value = 'Evelynn Hobbs';
options = [
'Tori Day',
'Ishaan Martinez',
'Alvin Bailey',
'Opal Morton',
'Evelynn Hobbs',
'Leila Pacheco',
'Fatima Shaw',
'Alexandria Huynh',
];
}
The <headwind-select>
component can bind value using NgModel
or FormControl
.
import { Component } from '@angular/core';
import {
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
} from '@favian/headwind-ui';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-select-model-binding-example',
standalone: true,
imports: [
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
FormsModule,
ReactiveFormsModule,
],
template: `
<div class="w-full max-w-[250px] space-y-2 rounded-xl bg-white p-8 shadow-xl">
<headwind-select
[(ngModel)]="value"
class="flex h-10 w-full cursor-pointer select-none items-center justify-between rounded-lg border bg-white px-3"
>
<div [class.text-zinc-400]="!value" class="text-sm">
{{ value ? value : 'Select Option' }}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
<ng-template headwindOptionsOverlay>
<headwind-options-overlay class="block max-h-[200px] overflow-auto rounded-md bg-white p-2 shadow-xl">
@for (option of options; track option) {
<headwind-option
#headwindOption
[class.text-rose-500]="headwindOption.selected"
[class.bg-rose-50]="headwindOption.focused"
[value]="option"
class="block cursor-pointer select-none px-3 py-2 text-sm hover:bg-rose-50"
>
{{ option }}
</headwind-option>
}
</headwind-options-overlay>
</ng-template>
</headwind-select>
<headwind-select
[formControl]="formControl"
class="flex h-10 w-full cursor-pointer select-none items-center justify-between rounded-lg border bg-white px-3"
>
<div [class.text-zinc-400]="!formControl.value" class="text-sm">
{{ formControl.value ? formControl.value : 'Select Option' }}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
<ng-template headwindOptionsOverlay>
<headwind-options-overlay class="block max-h-[200px] overflow-auto rounded-md bg-white p-2 shadow-xl">
@for (option of options; track option) {
<headwind-option
#headwindOption
[class.text-rose-500]="headwindOption.selected"
[class.bg-rose-50]="headwindOption.focused"
[value]="option"
class="block cursor-pointer select-none px-3 py-2 text-sm hover:bg-rose-50"
>
{{ option }}
</headwind-option>
}
</headwind-options-overlay>
</ng-template>
</headwind-select>
</div>
`,
})
export class SelectModelBindingExampleComponent {
value = '';
formControl = new FormControl('');
options = [
'Tori Day',
'Ishaan Martinez',
'Alvin Bailey',
'Opal Morton',
'Evelynn Hobbs',
'Leila Pacheco',
'Fatima Shaw',
'Alexandria Huynh',
];
}
Setting the disabled
attribute will disable the Select. Styling for the disabled state uses the [disabled]
CSS selector instead of :disabled
.
import { Component } from '@angular/core';
import {
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
} from '@favian/headwind-ui';
@Component({
selector: 'app-select-disabled-state-example',
standalone: true,
imports: [
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
],
template: `
<headwind-select
[(value)]="value"
disabled
class="inline-flex h-10 w-full max-w-[200px] cursor-pointer select-none items-center justify-between rounded-lg border bg-white px-3"
>
<div [class.text-zinc-400]="!value" class="placeholder text-sm">
{{ value ? value : 'Select Option' }}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
<ng-template headwindOptionsOverlay>
<headwind-options-overlay class="block max-h-[200px] overflow-auto rounded-md bg-white p-2 shadow-xl">
@for (option of options; track option) {
<headwind-option
#headwindOption
[class.text-rose-500]="headwindOption.selected"
[class.bg-rose-50]="headwindOption.focused"
[value]="option"
class="block cursor-pointer select-none px-3 py-2 text-sm hover:bg-rose-50"
>
{{ option }}
</headwind-option>
}
</headwind-options-overlay>
</ng-template>
</headwind-select>
`,
styles: [
`
@tailwind components;
@layer components {
.headwind-select[disabled] {
@apply cursor-not-allowed bg-zinc-100;
.placeholder {
@apply text-zinc-400;
}
svg {
@apply stroke-zinc-400;
}
}
}
`,
],
})
export class SelectDisabledStateExampleComponent {
value = 'Evelynn Hobbs';
options = [
'Tori Day',
'Ishaan Martinez',
'Alvin Bailey',
'Opal Morton',
'Evelynn Hobbs',
'Leila Pacheco',
'Fatima Shaw',
'Alexandria Huynh',
];
}
You can set the multiple
attribute on <headwind-select>
to enable selection of multiple options. The value must be in the form of an array, and if a non-array value is provided, the value is ignored and a new array is created.
import { Component } from '@angular/core';
import {
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
} from '@favian/headwind-ui';
@Component({
selector: 'app-select-multiple-example',
standalone: true,
imports: [
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
],
template: `
<div class="w-full max-w-[250px] space-y-4 rounded-xl bg-white p-8 shadow-xl">
<div class="space-y-2">
<div class="text-lg font-semibold">Selected...</div>
@if (value.length > 0) {
<ul class="list-inside list-disc space-y-1 text-sm">
@for (item of value; track item) {
<li>
{{ item }}
</li>
}
</ul>
} @else {
<div class="text-sm text-zinc-400">Nothing Selected</div>
}
</div>
<headwind-select
[(value)]="value"
multiple
class="inline-flex h-10 w-full cursor-pointer select-none items-center justify-between space-x-2 rounded-lg border bg-white px-3"
>
<div [class.text-zinc-400]="value.length === 0" class="truncate text-sm">
{{ value.length > 0 ? value : 'Select Option' }}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
<ng-template headwindOptionsOverlay>
<headwind-options-overlay class="block max-h-[200px] overflow-auto rounded-md bg-white p-2 shadow-xl">
@for (option of options; track option) {
<headwind-option
#headwindOption
[class.text-rose-500]="headwindOption.selected"
[class.bg-rose-50]="headwindOption.focused"
[value]="option"
class="block cursor-pointer select-none px-3 py-2 text-sm hover:bg-rose-50"
>
{{ option }}
</headwind-option>
}
</headwind-options-overlay>
</ng-template>
</headwind-select>
</div>
`,
})
export class SelectMultipleExampleComponent {
value: string[] = [];
options = [
'Tori Day',
'Ishaan Martinez',
'Alvin Bailey',
'Opal Morton',
'Evelynn Hobbs',
'Leila Pacheco',
'Fatima Shaw',
'Alexandria Huynh',
];
}
Bind an animation trigger to <headwind-options-overlay>
to play animation when options opened state is toggled.
import { Component } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';
import {
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
} from '@favian/headwind-ui';
@Component({
selector: 'app-select-animation-example',
standalone: true,
imports: [
HeadwindOptionComponent,
HeadwindOptionsOverlayComponent,
HeadwindOptionsOverlayDirective,
HeadwindSelectComponent,
],
template: `
<headwind-select
#select
class="inline-flex h-10 w-full max-w-[200px] cursor-pointer select-none items-center justify-between rounded-lg border bg-white px-3"
>
<div [class.text-zinc-400]="!select.value" class="text-sm">
{{ select.value ? select.value : 'Select Option' }}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-4 w-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
<ng-template headwindOptionsOverlay>
<headwind-options-overlay [@fadeIn] class="block max-h-[200px] overflow-auto rounded-md bg-white p-2 shadow-xl">
@for (option of options; track option) {
<headwind-option
#headwindOption
[class.text-rose-500]="headwindOption.selected"
[class.bg-rose-50]="headwindOption.focused"
[value]="option"
class="block cursor-pointer select-none px-3 py-2 text-sm hover:bg-rose-50"
>
{{ option }}
</headwind-option>
}
</headwind-options-overlay>
</ng-template>
</headwind-select>
`,
animations: [
trigger('fadeIn', [
state(
'void',
style({
opacity: 0,
}),
),
transition(
'void => *',
animate(
'.1s',
style({
opacity: 1,
}),
),
),
transition('* => void', animate('.1s')),
]),
],
})
export class SelectAnimationExampleComponent {
options = [
'Tori Day',
'Ishaan Martinez',
'Alvin Bailey',
'Opal Morton',
'Evelynn Hobbs',
'Leila Pacheco',
'Fatima Shaw',
'Alexandria Huynh',
];
}
Clicking on <headwind-select>
opens the options.<br/>
Clicking outside of <headwind-options-overlay>
closes the options overlay.<br/>
Clicking on <headwind-option>
changes the value of Select.
Command | Target | Description |
---|---|---|
Enter , ArrowDown , ArrowUp |
<headwind-select> |
Toggle the opened state of options. |
ArrowDown |
<headwind-options-overlay> |
Focus to the next option. |
ArrowUp |
<headwind-options-overlay> |
Focus to the previous option. |
Enter , Space |
<headwind-options-overlay> |
Select focused option. |
Escape |
window |
Close the opened options. |
A component that toggles <headwind-options-overlay>
and reflects the selected value.
<headwind-select>
.headwind-select
Name | Description |
---|---|
@Input() set value(value: any) |
Set the selected value of Select. |
@Input() set multiple(value: boolean) |
Set to select multiple options. |
@Input() set disabled(value: boolean) |
Set the disabled state of Select. |
Name | Description |
---|---|
@Output() valueChange: EventEmitter<any> |
Emits the changed value of Select. |
Name | Description |
---|---|
get opened(): boolean |
Get the opened state of options. |
Name | Description |
---|---|
open() |
Open the options. If it is already opened, it is ignored. |
close() |
Select the next option. If it is already closed, it is ignored. |
A directive for the template that wraps <headwind-options-overlay>
.
ng-template[headwindOptionsOverlay]
An options overlay component containing multiple <headwind-option>
.
<headwind-options-overlay>
.headwind-options-overlay
Name | Description |
---|---|
@Output() actualDirectionChange: EventEmitter<YDirection> |
The direction in which the actual options overlay is rendered is emitted every RequestAnimationFrame . |
An option component whose value can be reflected in <headwind-select>
when selected.
<headwind-option>
.headwind-option
<br/>
.headwind-selected
when option selected.
.headwind-focused
when option focused.
Name | Description |
---|---|
@Input() value: any |
Set the value of option. |
Name | Description |
---|---|
get focused(): boolean |
Focused state of option. |
get selected(): boolean |
Selected state of option. |