action-compact-button
v15.0.5
Published
This is an Angular Module containing Components/Services using Material
Downloads
7
Readme
Action Compact Button Component
Overview
The action-compact-button library provides a compact version of action buttons with expand/collapse functionality. It displays buttons in a space-efficient manner, showing only icons in compact mode and expanding to show full labels when needed. The component implements Angular's ControlValueAccessor interface for seamless form integration.
Core Capabilities
🎯 Compact Action Button Interface
- Space-Efficient Design: Displays buttons in compact mode (icons only) or expanded mode (with labels)
- Expandable Interface: Toggle between compact and expanded views dynamically
- Material Design Integration: Uses Angular Material components for consistent styling
- Form Control Support: Implements ControlValueAccessor for reactive forms
- Badge Support: Maintains notification badge functionality in both modes
- Responsive Behavior: Adapts to available screen space
🔧 Features
✅ Expand/Collapse Toggle - Dynamic view switching between compact and expanded
✅ ControlValueAccessor Implementation - Works with Angular forms
✅ Material Design Integration - Uses Angular Material components
✅ Badge Notifications - Visual notification indicators in both modes
✅ Icon-Only Display - Compact mode shows only icons to save space
✅ Label Display - Expanded mode shows both icons and labels
✅ State Management - Disabled states and conditional rendering
✅ Responsive Design - Adapts to different screen sizes
Key Benefits
| Feature | Description | |---------|-------------| | Space Optimization | Compact mode saves horizontal space | | Flexible Display | Toggle between icon-only and labeled views | | Form Integration | Native Angular form control support | | Badge Support | Notification indicators work in both modes | | Consistent Styling | Material Design theming throughout |
Demo Component (ActionCompactButtonDemoComponent)
The demo component showcases the expand/collapse functionality and various button configurations.
Usage
To use the demo component in your application:
<app-compact-button-actions-demo></app-compact-button-actions-demo>The demo includes:
- Expandable/collapsible button interface
- Phone button (disabled state)
- Messages button (with badge showing 6 notifications)
- Alerts button (zero badge state)
- Material checkbox to control expansion state
- Different color configurations
Summary
The action-compact-button library provides a space-efficient, expandable action button component that dynamically switches between compact (icon-only) and expanded (icon+label) display modes.
Quick Start Guide
Installation & Setup (2 minutes)
1. Import Module
// app.module.ts
import { ActionCompactButtonModule } from 'action-compact-button';
@NgModule({
imports: [
ActionCompactButtonModule
]
})
export class AppModule { }2. No Module Configuration Required
The ActionCompactButtonModule does not require global configuration. Components can be used immediately after module import.
Quick Examples
Example 1: Basic Compact/Expand Buttons
import { Component } from '@angular/core';
import { ButtonAction } from 'action-compact-button';
@Component({
selector: 'app-compact-buttons',
template: `
<div class="button-container">
<app-compact-button-actions
[data]="buttonActions"
[expand]="isExpanded">
</app-compact-button-actions>
<button (click)="toggleExpansion()">
{{ isExpanded ? 'Compact' : 'Expand' }} View
</button>
</div>
`
})
export class CompactButtonsComponent {
isExpanded = false;
buttonActions: ButtonAction[] = [
{ icon: 'dialpad', label: 'Phone', disabled: true },
{ icon: 'voicemail', label: 'Messages', badge: 6, color: 'orange' },
{ icon: 'notifications_off', label: 'Alerts', badge: 0 }
];
toggleExpansion() {
this.isExpanded = !this.isExpanded;
}
}Example 2: Form Control Integration
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { ButtonAction } from 'action-compact-button';
@Component({
selector: 'app-form-compact-buttons',
template: `
<app-compact-button-actions
[formControl]="compactControl"
[data]="buttonActions"
[expand]="isExpanded">
</app-compact-button-actions>
<div class="controls">
<label>
<input type="checkbox" [(ngModel)]="isExpanded">
Expand Buttons
</label>
</div>
<div>Selected: {{ compactControl.value | json }}</div>
`
})
export class FormCompactButtonsComponent {
isExpanded = true;
compactControl = new FormControl();
buttonActions: ButtonAction[] = [
{ icon: 'edit', label: 'Edit', color: 'primary' },
{ icon: 'delete', label: 'Delete', color: 'warn' },
{ icon: 'share', label: 'Share', color: 'accent' }
];
}Example 3: Responsive Button Panel
import { Component } from '@angular/core';
import { ButtonAction } from 'action-compact-button';
@Component({
selector: 'app-responsive-buttons',
template: `
<div class="responsive-container">
<h3>Action Panel</h3>
<app-compact-button-actions
[data]="actionButtons"
[expand]="isExpanded"
[disabled]="isDisabled">
</app-compact-button-actions>
<div class="controls">
<button (click)="toggleExpanded()">
{{ isExpanded ? 'Show Compact' : 'Show Labels' }}
</button>
<button (click)="toggleDisabled()">
{{ isDisabled ? 'Enable' : 'Disable' }}
</button>
</div>
<div class="status">
Mode: {{ isExpanded ? 'Expanded' : 'Compact' }} |
State: {{ isDisabled ? 'Disabled' : 'Enabled' }}
</div>
</div>
`,
styles: [`
.responsive-container {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
}
.controls {
margin-top: 1rem;
display: flex;
gap: 0.5rem;
}
.status {
margin-top: 0.5rem;
font-size: 0.9rem;
color: #666;
}
`]
})
export class ResponsiveButtonsComponent {
isExpanded = false;
isDisabled = false;
actionButtons: ButtonAction[] = [
{ icon: 'save', label: 'Save', color: 'primary' },
{ icon: 'cancel', label: 'Cancel', color: 'warn' },
{ icon: 'help', label: 'Help', badge: 3 }
];
toggleExpanded() {
this.isExpanded = !this.isExpanded;
}
toggleDisabled() {
this.isDisabled = !this.isDisabled;
}
}Example 4: Dashboard Action Buttons
import { Component } from '@angular/core';
import { ButtonAction } from 'action-compact-button';
@Component({
selector: 'app-dashboard-actions',
template: `
<div class="dashboard">
<div class="header">
<h2>Dashboard</h2>
<app-compact-button-actions
[data]="dashboardActions"
[expand]="showLabels"
(selectionChange)="handleAction($event)">
</app-compact-button-actions>
</div>
<div class="content">
<p>Dashboard content goes here...</p>
</div>
</div>
`,
styles: [`
.dashboard { padding: 1rem; }
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.header h2 { margin: 0; }
`]
})
export class DashboardActionsComponent {
showLabels = false;
dashboardActions: ButtonAction[] = [
{ icon: 'refresh', label: 'Refresh', color: 'primary' },
{ icon: 'settings', label: 'Settings', color: 'accent' },
{ icon: 'notifications', label: 'Notifications', badge: this.getNotificationCount() },
{ icon: 'help', label: 'Help', color: 'warn' }
];
getNotificationCount(): number {
// Logic to get notification count
return 7;
}
handleAction(action: ButtonAction) {
console.log('Action triggered:', action);
// Handle different actions based on icon/label
switch (action.icon) {
case 'refresh':
this.refreshDashboard();
break;
case 'settings':
this.openSettings();
break;
case 'notifications':
this.showNotifications();
break;
case 'help':
this.showHelp();
break;
}
}
private refreshDashboard() {
console.log('Refreshing dashboard...');
}
private openSettings() {
console.log('Opening settings...');
}
private showNotifications() {
console.log('Showing notifications...');
}
private showHelp() {
console.log('Showing help...');
}
}Component API
Inputs
| Input | Type | Description | Default |
| :--- | :--- | :--- | :--- |
| data | ButtonAction[] | Array of button action configurations | [] |
| expand | boolean \| string | Controls expansion state (true/expanded, false/compact) | false |
| disabled | boolean | Whether the entire component is disabled | false |
Outputs
| Output | Type | Description |
| :--- | :--- | :--- |
| selectionChange | EventEmitter<any> | Emits when a button is selected/clicked |
Model Structures
ButtonAction Interface (Shared)
The action-compact-button package uses the same ButtonAction model as action-buttons:
export interface ButtonActionInterface {
label?: string; // Button text label
icon?: string; // Material icon name or custom icon
disabled?: boolean; // Individual button disabled state
badge?: number; // Notification badge count (0 or positive number)
color?: string; // Custom color (CSS color value)
svg?: boolean; // Whether icon is SVG format
}ButtonAction Class (Shared)
export class ButtonAction implements ButtonActionInterface {
constructor(
public label?: string,
public icon?: string,
public disabled?: boolean = false,
public badge?: number = 0,
public color?: string,
public svg?: boolean = false
) {}
static adapt(data?: any): ButtonAction {
return new ButtonAction(
data?.label,
data?.icon,
(data?.disabled) ? data.disabled : false,
(data?.badge) ? data.badge : 0,
data?.color,
(data?.svg) ? true : false
);
}
}Usage Examples
// Compact mode buttons (icons more important)
const compactButtons: ButtonAction[] = [
{ icon: 'home', label: 'Home', color: 'primary' },
{ icon: 'search', label: 'Search', color: 'accent' },
{ icon: 'notifications', label: 'Alerts', badge: 5 },
{ icon: 'settings', label: 'Settings', color: 'warn' }
];
// Expanded mode buttons (labels more important)
const expandedButtons: ButtonAction[] = [
{ icon: 'save', label: 'Save Document', color: 'primary' },
{ icon: 'print', label: 'Print Report', color: 'accent' },
{ icon: 'email', label: 'Email Results', badge: 2 },
{ icon: 'download', label: 'Download Data', color: 'warn' }
];
// Mixed usage - some buttons work better compact, others expanded
const mixedButtons: ButtonAction[] = [
{ icon: 'home', label: 'Dashboard' }, // Good for both modes
{ icon: 'notifications', label: 'Alerts', badge: 3 }, // Badge visible in both modes
{ icon: 'settings', label: 'Settings' }, // Settings icon is descriptive
{ icon: 'book', label: 'Documentation' } // Book icon needs label in compact mode
];Module Configuration
ActionCompactButtonModule
No Global Configuration Required
The ActionCompactButtonModule does not provide a forRoot() method or global configuration options. All configuration is done at the component level through input properties.
Module Structure
@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
MatIconModule,
MatButtonModule,
MatBadgeModule,
],
declarations: [
ActionCompactButtonComponent,
ActionCompactButtonDemoComponent,
],
exports:[
ActionCompactButtonComponent,
ActionCompactButtonDemoComponent,
]
})
export class ActionCompactButtonModule { }Dependencies
- @angular/common: Core Angular functionality
- @angular/forms: Form control integration
- @angular/material: Material Design components
- MatIconModule: Icon display
- MatButtonModule: Button base component
- MatBadgeModule: Badge notifications
- Shared Models: Uses
ButtonActionmodel fromaction-buttonspackage
Styling and Customization
CSS Classes and Styling
The component uses Material Design styling and can be customized using:
- Global Material Theme: Configure colors in your Angular Material theme
- Component-specific Styles: Add custom CSS classes for different display modes
- Responsive Breakpoints: Customize when to show compact vs expanded mode
- Color Input: Use the
colorproperty for individual button styling
Example Customization
// Custom styles for compact button actions
:host ::ng-deep .compact-button-actions {
&.compact-mode {
.mat-button-wrapper {
padding: 8px; // Smaller padding in compact mode
}
.button-label {
display: none; // Hide labels in compact mode
}
}
&.expanded-mode {
.mat-button-wrapper {
padding: 12px 16px; // Larger padding in expanded mode
}
.button-label {
display: inline-block;
margin-left: 8px;
font-size: 0.875rem;
}
}
// Animation for expand/collapse transition
.mat-button {
transition: all 0.3s ease;
}
}Responsive Behavior
// Auto-expand on desktop, compact on mobile
:host ::ng-deep .compact-button-actions {
@media (min-width: 768px) {
&[expand="false"] {
// Force expanded mode on larger screens
.button-label {
display: inline-block !important;
}
}
}
@media (max-width: 767px) {
// Compact mode on mobile
&[expand="false"] {
.button-label {
display: none;
}
}
}
}Accessibility
ARIA Support
- Buttons include proper ARIA labels in both compact and expanded modes
- Keyboard navigation is supported across all button states
- Screen reader friendly with appropriate labels and descriptions
- Badge notifications include appropriate ARIA live regions
- Expand/collapse state is communicated to assistive technologies
Best Practices
- Provide meaningful labels for all buttons (used in expanded mode)
- Use descriptive icons that work well in compact mode
- Set badge counts appropriately for notification indicators
- Consider screen size when implementing expand/collapse logic
- Test with screen readers in both compact and expanded modes
- Maintain consistent spacing between compact and expanded layouts
Integration Examples
With Screen Observer
import { Component } from '@angular/core';
import { ScreenObserverService } from 'screen-observer';
@Component({
template: `
<app-compact-button-actions
[data]="actionButtons"
[expand]="shouldExpand">
</app-compact-button-actions>
`
})
export class ResponsiveActionsComponent {
shouldExpand = false;
constructor(private screenObserver: ScreenObserverService) {
this.screenObserver.screenSize$.subscribe(screenType => {
// Expand on tablets and desktop, compact on mobile
this.shouldExpand = ['TC52', 'Mobile'].indexOf(screenType) === -1;
});
}
actionButtons: ButtonAction[] = [
{ icon: 'home', label: 'Home' },
{ icon: 'search', label: 'Search' },
{ icon: 'notifications', label: 'Alerts', badge: 3 }
];
}With State Management
// Integration with HTTP Request Manager
@Component({
template: `
<app-compact-button-actions
[data]="actionButtons$ | async"
[expand]="uiState.expanded"
(selectionChange)="handleAction($event)">
</app-compact-button-actions>
`
})
export class StateManagedCompactButtonsComponent {
actionButtons$ = this.actionStore.buttons$;
uiState = { expanded: false };
constructor(private actionStore: ActionStore) {}
handleAction(action: ButtonAction) {
this.actionStore.executeAction(action);
}
}With Column Fitter
import { Component } from '@angular/core';
import { ColumnFitterService } from 'column-fitter';
@Component({
template: `
<div [style.gridTemplateColumns]="columnTemplate">
<app-compact-button-actions
[data]="actionButtons"
[expand]="columnsFit > 2">
</app-compact-button-actions>
</div>
`
})
export class AdaptiveActionsComponent {
columnsFit = 1;
columnTemplate = '1fr';
constructor(private columnFitter: ColumnFitterService) {
this.columnFitter.columns$.subscribe(columns => {
this.columnsFit = columns;
this.columnTemplate = `repeat(${columns}, 1fr)`;
});
}
actionButtons: ButtonAction[] = [
{ icon: 'add', label: 'Add Item' },
{ icon: 'edit', label: 'Edit' },
{ icon: 'delete', label: 'Delete' },
{ icon: 'share', label: 'Share' }
];
}Testing
Unit Testing Example
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActionCompactButtonComponent } from './action-compact-button.component';
import { ButtonAction } from './models';
describe('ActionCompactButtonComponent', () => {
let component: ActionCompactButtonComponent;
let fixture: ComponentFixture<ActionCompactButtonComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ActionCompactButtonComponent ]
}).compileComponents();
fixture = TestBed.createComponent(ActionCompactButtonComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should display buttons in compact mode when expand is false', () => {
component.data = [{ label: 'Test', icon: 'home' }];
component.expand = false;
fixture.detectChanges();
const compiled = fixture.nativeElement;
const labels = compiled.querySelectorAll('.button-label');
expect(labels.length).toBe(0); // Labels hidden in compact mode
});
it('should display buttons in expanded mode when expand is true', () => {
component.data = [{ label: 'Test', icon: 'home' }];
component.expand = true;
fixture.detectChanges();
const compiled = fixture.nativeElement;
const labels = compiled.querySelectorAll('.button-label');
expect(labels.length).toBe(1); // Labels shown in expanded mode
});
it('should toggle expand state correctly', () => {
component.data = [{ label: 'Test', icon: 'home' }];
component.expand = false;
fixture.detectChanges();
expect(component.expand).toBe(false);
component.expand = true;
fixture.detectChanges();
expect(component.expand).toBe(true);
});
it('should emit selection change when button is clicked', () => {
spyOn(component.selectionChange, 'emit');
component.data = [{ label: 'Test', icon: 'home' }];
fixture.detectChanges();
const button = fixture.nativeElement.querySelector('button');
button.click();
expect(component.selectionChange.emit).toHaveBeenCalled();
});
});Troubleshooting
Common Issues
- Icons not displaying: Ensure Material Icons are loaded in your index.html
- Labels not showing in expanded mode: Check that expand input is set to true
- Compact mode not working: Verify expand input is set to false or not provided
- Form control not working: Verify ReactiveFormsModule is imported
- Badge not showing: Make sure MatBadgeModule is imported in your module
Performance Tips
- Use OnPush change detection for better performance with large button arrays
- Implement trackBy for dynamic button lists
- Avoid frequent data object recreation to prevent unnecessary re-renders
- Use immutable data patterns for button action updates
- Consider debouncing expand/collapse state changes for smooth animations
Debug Mode
// Add debugging to track expand/collapse state
@Component({
template: `
<div class="debug-info">
Expand State: {{ expand }} |
Data Length: {{ data?.length || 0 }} |
Disabled: {{ disabled }}
</div>
<app-compact-button-actions
[data]="data"
[expand]="expand"
[disabled]="disabled">
</app-compact-button-actions>
`
})
export class DebugCompactButtonsComponent {
expand = false;
disabled = false;
data: ButtonAction[] = [];
}