- 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
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Icon
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Label
- Menubar
- Native Select
- 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
Drawer
A dialog that slides in from the bottom of the screen. Designed after the macOS/iOS bottom sheet pattern.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { HlmButtonImports } from '@spartan-ng/helm/button';
import { HlmDrawerImports } from '@spartan-ng/helm/drawer';
import { HlmFieldImports } from '@spartan-ng/helm/field';
import { HlmInputImports } from '@spartan-ng/helm/input';
@Component({
selector: 'spartan-drawer-preview',
imports: [HlmDrawerImports, HlmButtonImports, HlmFieldImports, HlmInputImports],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<hlm-drawer>
<button id="edit-profile" hlmDrawerTrigger hlmBtn variant="outline">Open Drawer</button>
<hlm-drawer-content *hlmDrawerPortal="let ctx">
<hlm-drawer-header>
<h3 hlmDrawerTitle>Edit Profile</h3>
<p hlmDrawerDescription>Make changes to your profile here. Click save when you're done.</p>
</hlm-drawer-header>
<hlm-field-group class="px-4">
<hlm-field>
<label hlmFieldLabel for="name">Name</label>
<input hlmInput id="name" value="Pedro Duarte" />
</hlm-field>
<hlm-field>
<label hlmFieldLabel for="username">Username</label>
<input hlmInput id="username" value="peduarte" />
</hlm-field>
</hlm-field-group>
<hlm-drawer-footer>
<button hlmBtn type="submit">Save Changes</button>
<button hlmDrawerClose hlmBtn variant="outline">Cancel</button>
</hlm-drawer-footer>
</hlm-drawer-content>
</hlm-drawer>
`,
})
export class DrawerPreview {}Installation
ng g @spartan-ng/cli:ui drawernx g @spartan-ng/cli:ui drawerUsage
import { HlmDrawerImports } from '@spartan-ng/helm/drawer';<hlm-drawer>
<button hlmDrawerTrigger hlmBtn variant="outline">Open</button>
<hlm-drawer-content *hlmDrawerPortal="let ctx">
<hlm-drawer-header>
<h3 hlmDrawerTitle>Are you absolutely sure?</h3>
<p hlmDrawerDescription>This action cannot be undone.</p>
</hlm-drawer-header>
</hlm-drawer-content>
</hlm-drawer>Examples
Direction
You can change the direction the drawer slides in from using the direction input on the trigger.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { HlmButtonImports } from '@spartan-ng/helm/button';
import { HlmDrawerImports } from '@spartan-ng/helm/drawer';
@Component({
selector: 'spartan-drawer-direction-preview',
imports: [HlmDrawerImports, HlmButtonImports],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<hlm-drawer>
<div class="flex flex-wrap gap-2">
<button id="bottom" hlmDrawerTrigger direction="bottom" hlmBtn variant="outline">Bottom</button>
<button id="top" hlmDrawerTrigger direction="top" hlmBtn variant="outline">Top</button>
<button id="left" hlmDrawerTrigger direction="left" hlmBtn variant="outline">Left</button>
<button id="right" hlmDrawerTrigger direction="right" hlmBtn variant="outline">Right</button>
</div>
<hlm-drawer-content *hlmDrawerPortal="let ctx">
<hlm-drawer-header>
<h3 hlmDrawerTitle>Edit profile</h3>
<p hlmDrawerDescription>Make changes to your profile here. Click save when you're done.</p>
</hlm-drawer-header>
<div class="px-4">
@for (item of _items; track item) {
<p class="mb-4 leading-normal">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</p>
}
</div>
<hlm-drawer-footer>
<button hlmBtn>Save changes</button>
<button hlmBtn variant="outline" hlmDrawerClose>Cancel</button>
</hlm-drawer-footer>
</hlm-drawer-content>
</hlm-drawer>
`,
})
export class DrawerDirectionPreview {
protected readonly _items = Array.from({ length: 3 }, (_, i) => i + 1);
}Nested
Drawers can be nested within each other to create multi-step or drill-down experiences. Use a nested hlm-drawer inside the content of another drawer.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { HlmButtonImports } from '@spartan-ng/helm/button';
import { HlmDrawerImports } from '@spartan-ng/helm/drawer';
@Component({
selector: 'spartan-drawer-nested-preview',
imports: [HlmDrawerImports, HlmButtonImports],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<hlm-drawer>
<button hlmDrawerTrigger hlmBtn variant="outline">Open Drawer</button>
<hlm-drawer-content *hlmDrawerPortal="let ctx">
<hlm-drawer-header>
<h3 hlmDrawerTitle>Profile Settings</h3>
<p hlmDrawerDescription>Manage your account settings.</p>
</hlm-drawer-header>
<div class="px-4">
<p class="mb-4 leading-normal">This is the first drawer. You can open another drawer on top of this one.</p>
<hlm-drawer>
<button hlmDrawerTrigger hlmBtn variant="outline">Open Nested</button>
<hlm-drawer-content *hlmDrawerPortal="let nestedCtx">
<hlm-drawer-header>
<h3 hlmDrawerTitle>Nested Drawer</h3>
<p hlmDrawerDescription>This drawer is nested inside the first one.</p>
</hlm-drawer-header>
<div class="px-4">
<p class="mb-4 leading-normal">Nested drawers allow you to layer content without losing context.</p>
</div>
<hlm-drawer-footer>
<button hlmDrawerClose hlmBtn variant="outline">Close</button>
</hlm-drawer-footer>
</hlm-drawer-content>
</hlm-drawer>
</div>
<hlm-drawer-footer>
<button hlmDrawerClose hlmBtn variant="outline">Close</button>
</hlm-drawer-footer>
</hlm-drawer-content>
</hlm-drawer>
`,
})
export class DrawerNestedPreview {}RTL
To enable RTL support in spartan-ng, see the RTL configuration guide.
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';
import { TranslateService, Translations } from '@spartan-ng/app/app/shared/translate.service';
import { HlmButtonImports } from '@spartan-ng/helm/button';
import { HlmDrawerImports } from '@spartan-ng/helm/drawer';
import { HlmFieldImports } from '@spartan-ng/helm/field';
import { HlmInputImports } from '@spartan-ng/helm/input';
@Component({
selector: 'spartan-drawer-rtl',
imports: [HlmDrawerImports, HlmFieldImports, HlmButtonImports, HlmInputImports],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<hlm-drawer [dir]="_dir()">
<button hlmDrawerTrigger hlmBtn variant="outline">{{ _t()['open'] }}</button>
<hlm-drawer-content *hlmDrawerPortal="let ctx" [dir]="_dir()">
<hlm-drawer-header>
<h3 hlmDrawerTitle>{{ _t()['editProfile'] }}</h3>
<p hlmDrawerDescription>{{ _t()['description'] }}</p>
</hlm-drawer-header>
<hlm-field-group class="px-4">
<hlm-field>
<label hlmFieldLabel for="name-rtl">{{ _t()['name'] }}</label>
<input hlmInput id="name-rtl" value="Pedro Duarte" />
</hlm-field>
<hlm-field>
<label hlmFieldLabel for="username-rtl">{{ _t()['username'] }}</label>
<input hlmInput id="username-rtl" value="peduarte" />
</hlm-field>
</hlm-field-group>
<hlm-drawer-footer>
<button hlmBtn type="submit">{{ _t()['save'] }}</button>
<button hlmDrawerClose hlmBtn variant="outline">{{ _t()['close'] }}</button>
</hlm-drawer-footer>
</hlm-drawer-content>
</hlm-drawer>
`,
})
export class DrawerRtl {
private readonly _language = inject(TranslateService).language;
private readonly _translations: Translations = {
en: {
dir: 'ltr',
values: {
open: 'Open',
editProfile: 'Edit profile',
description: "Make changes to your profile here. Click save when you're done.",
name: 'Name',
username: 'Username',
save: 'Save changes',
close: 'Close',
},
},
ar: {
dir: 'rtl',
values: {
open: 'فتح',
editProfile: 'تعديل الملف الشخصي',
description: 'قم بإجراء تغييرات على ملفك الشخصي هنا. انقر حفظ عند الانتهاء.',
name: 'الاسم',
username: 'اسم المستخدم',
save: 'حفظ التغييرات',
close: 'إغلاق',
},
},
he: {
dir: 'rtl',
values: {
open: 'פתח',
editProfile: 'עריכת פרופיל',
description: 'בצע שינויים בפרופיל שלך כאן. לחץ שמור כשתסיים.',
name: 'שם',
username: 'שם משתמש',
save: 'שמור שינויים',
close: 'סגור',
},
},
};
private readonly _translation = computed(() => this._translations[this._language()]);
protected readonly _t = computed(() => this._translation().values);
protected readonly _dir = computed(() => this._translation().dir);
}Brain API
BrnDrawerClose
Selector: button[brnDrawerClose]
BrnDrawerContent
Selector: [brnDrawerContent]
BrnDrawerDescription
Selector: [brnDrawerDescription]
BrnDrawerHandle
Selector: [brnDrawerHandle]
Inputs
| Prop | Type | Default | Description |
|---|---|---|---|
| closeThreshold | number | DEFAULT_CLOSE_THRESHOLD | - |
BrnDrawerOverlay
Selector: [brnDrawerOverlay],brn-drawer-overlay
BrnDrawerTitle
Selector: [brnDrawerTitle]
BrnDrawerTrigger
Selector: button[brnDrawerTrigger]
Inputs
| Prop | Type | Default | Description |
|---|---|---|---|
| direction | 'bottom' | 'top' | 'left' | 'right' | undefined | undefined | - |
BrnDrawer
Selector: [brnDrawer],brn-drawer
ExportAs: brnDrawer
Inputs
| Prop | Type | Default | Description |
|---|---|---|---|
| direction | 'bottom' | 'top' | 'left' | 'right' | bottom | - |
Helm API
HlmDrawerClose
Selector: button[hlmDrawerClose]
HlmDrawerContent
Selector: hlm-drawer-content
HlmDrawerDescription
Selector: [hlmDrawerDescription]
HlmDrawerFooter
Selector: [hlmDrawerFooter],hlm-drawer-footer
HlmDrawerHeader
Selector: [hlmDrawerHeader],hlm-drawer-header
HlmDrawerOverlay
Selector: [hlmDrawerOverlay],hlm-drawer-overlay
Inputs
| Prop | Type | Default | Description |
|---|---|---|---|
| class | ClassValue | - | - |
HlmDrawerPortal
Selector: [hlmDrawerPortal]
HlmDrawerTitle
Selector: [hlmDrawerTitle]
HlmDrawerTrigger
Selector: button[hlmDrawerTrigger]
HlmDrawer
Selector: hlm-drawer
ExportAs: hlmDrawer
On This Page