Spartans get ready! v1 is coming!
We are very close to our first stable release. Expect more announcements in the coming weeks. v1 was made possible by our partner Zerops.
Getting Started
Stack
Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Autocomplete
- Avatar
- Badge
- Breadcrumb
- Button
- Button Group
- Calendar
- Card
- Carousel
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Dropdown Menu
- Empty
- Field
- Form Field
- Hover Card
- Icon
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Label
- Menubar
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner (Toast)
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Toggle Group
- Tooltip
Combobox
Autocomplete input and command palette with a list of suggestions.
import { Component, signal } from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideCheck, lucideChevronsUpDown, lucideSearch } from '@ng-icons/lucide';
import { BrnCommandImports } from '@spartan-ng/brain/command';
import { BrnPopoverImports } from '@spartan-ng/brain/popover';
import { HlmButtonImports } from '@spartan-ng/helm/button';
import { HlmCommandImports } from '@spartan-ng/helm/command';
import { HlmIconImports } from '@spartan-ng/helm/icon';
import { HlmPopoverImports } from '@spartan-ng/helm/popover';
type Framework = { label: string; value: string };
@Component({
selector: 'spartan-combobox-preview',
imports: [
BrnCommandImports,
HlmCommandImports,
NgIcon,
HlmIconImports,
HlmButtonImports,
BrnPopoverImports,
HlmPopoverImports,
],
providers: [provideIcons({ lucideChevronsUpDown, lucideSearch, lucideCheck })],
template: `
<brn-popover [state]="state()" (stateChanged)="stateChanged($event)" sideOffset="5">
<button
class="w-[200px] justify-between"
id="edit-profile"
variant="outline"
brnPopoverTrigger
(click)="state.set('open')"
hlmBtn
>
{{ currentFramework() ? currentFramework()?.label : 'Select framework...' }}
<ng-icon hlm size="sm" name="lucideChevronsUpDown" class="opacity-50" />
</button>
<hlm-command *brnPopoverContent="let ctx" hlmPopoverContent class="w-[200px] p-0">
<hlm-command-search>
<ng-icon hlm name="lucideSearch" />
<input placeholder="Search framework..." hlm-command-search-input />
</hlm-command-search>
<div *brnCommandEmpty hlmCommandEmpty>No results found.</div>
<hlm-command-list>
<hlm-command-group>
@for (framework of frameworks; track framework) {
<button hlm-command-item [value]="framework.value" (selected)="commandSelected(framework)">
<span>{{ framework.label }}</span>
<ng-icon
hlm
class="ml-auto"
[class.opacity-0]="currentFramework()?.value !== framework.value"
name="lucideCheck"
hlmCommandIcon
/>
</button>
}
</hlm-command-group>
</hlm-command-list>
</hlm-command>
</brn-popover>
`,
})
export class ComboboxPreview {
public frameworks = [
{
label: 'AnalogJs',
value: 'analogjs',
},
{
label: 'Angular',
value: 'angular',
},
{
label: 'Vue',
value: 'vue',
},
{
label: 'Nuxt',
value: 'nuxt',
},
{
label: 'React',
value: 'react',
},
{
label: 'NextJs',
value: 'nextjs',
},
];
public readonly currentFramework = signal<Framework | undefined>(undefined);
public readonly state = signal<'closed' | 'open'>('closed');
stateChanged(state: 'open' | 'closed') {
this.state.set(state);
}
commandSelected(framework: Framework) {
this.state.set('closed');
if (this.currentFramework()?.value === framework.value) {
this.currentFramework.set(undefined);
} else {
this.currentFramework.set(framework);
}
}
}Installation
The Combobox is built using a composition of the brn-popover and the brn-command components.
See installation instructions for the Popover and the Command components.
Usage
import { Component, signal } from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideCheck, lucideChevronsUpDown, lucideSearch } from '@ng-icons/lucide';
import { BrnCommandImports } from '@spartan-ng/brain/command';
import { BrnPopoverImports } from '@spartan-ng/brain/popover';
import { HlmButtonImports } from '@spartan-ng/helm/button';
import { HlmCommandImports } from '@spartan-ng/helm/command';
import { HlmIconImports } from '@spartan-ng/helm/icon';
import { HlmPopoverImports } from '@spartan-ng/helm/popover';
type Framework = { label: string; value: string };
@Component({
selector: 'spartan-combobox-preview',
imports: [
BrnCommandImports,
HlmCommandImports,
NgIcon,
HlmIconImports,
HlmButtonImports,
BrnPopoverImports,
HlmPopoverImports,
],
providers: [provideIcons({ lucideChevronsUpDown, lucideSearch, lucideCheck })],
template: `
<brn-popover [state]="state()" (stateChanged)="stateChanged($event)" sideOffset="5">
<button
class="w-[200px] justify-between"
id="edit-profile"
variant="outline"
brnPopoverTrigger
(click)="state.set('open')"
hlmBtn
>
{{ currentFramework() ? currentFramework()?.label : 'Select framework...' }}
<ng-icon hlm size="sm" name="lucideChevronsUpDown" class="opacity-50" />
</button>
<hlm-command *brnPopoverContent="let ctx" hlmPopoverContent class="w-[200px] p-0">
<hlm-command-search>
<ng-icon hlm name="lucideSearch" />
<input placeholder="Search framework..." hlm-command-search-input />
</hlm-command-search>
<div *brnCommandEmpty hlmCommandEmpty>No results found.</div>
<hlm-command-list>
<hlm-command-group>
@for (framework of frameworks; track framework) {
<button hlm-command-item [value]="framework.value" (selected)="commandSelected(framework)">
<span>{{ framework.label }}</span>
<ng-icon
hlm
class="ml-auto"
[class.opacity-0]="currentFramework()?.value !== framework.value"
name="lucideCheck"
hlmCommandIcon
/>
</button>
}
</hlm-command-group>
</hlm-command-list>
</hlm-command>
</brn-popover>
`,
})
export class ComboboxPreview {
public frameworks = [
{
label: 'AnalogJs',
value: 'analogjs',
},
{
label: 'Angular',
value: 'angular',
},
{
label: 'Vue',
value: 'vue',
},
{
label: 'Nuxt',
value: 'nuxt',
},
{
label: 'React',
value: 'react',
},
{
label: 'NextJs',
value: 'nextjs',
},
];
public readonly currentFramework = signal<Framework | undefined>(undefined);
public readonly state = signal<'closed' | 'open'>('closed');
stateChanged(state: 'open' | 'closed') {
this.state.set(state);
}
commandSelected(framework: Framework) {
this.state.set('closed');
if (this.currentFramework()?.value === framework.value) {
this.currentFramework.set(undefined);
} else {
this.currentFramework.set(framework);
}
}
}