Files
ui-essentials/projects/demo-ui-essentials/src/app/demos/command-palette-demo/command-palette-demo.component.ts
skyai_dev 2bbbf1b9f1 Add comprehensive UI component expansion
- Add new components: enhanced-table, fab-menu, icon-button, snackbar, select, tag-input, bottom-navigation, stepper, command-palette, floating-toolbar, transfer-list
- Refactor table-actions and select components
- Update component styles and exports
- Add corresponding demo components

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-03 21:50:45 +10:00

574 lines
17 KiB
TypeScript

import { Component, OnInit, OnDestroy, ViewChild, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Router } from '@angular/router';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import {
faHome,
faCog,
faUser,
faSearch,
faFile,
faEdit,
faEye,
faTools,
faQuestion,
faPlus,
faSave,
faCopy,
faPaste,
faUndo,
faRedo,
faKeyboard,
faPalette,
faRocket
} from '@fortawesome/free-solid-svg-icons';
import { Command, CommandCategory, CommandExecutionContext, CommandPaletteComponent, CommandPaletteService, createShortcuts, GlobalKeyboardDirective } from '../../../../../ui-essentials/src/lib/components/overlays/command-palette';
@Component({
selector: 'ui-command-palette-demo',
standalone: true,
imports: [
CommonModule,
FontAwesomeModule,
CommandPaletteComponent,
GlobalKeyboardDirective
],
template: `
<div class="demo-container" uiGlobalKeyboard [shortcuts]="globalShortcuts" (shortcutTriggered)="handleGlobalShortcut($event)">
<div class="demo-header">
<h2 class="demo-title">
<fa-icon [icon]="faPalette" class="demo-title-icon"></fa-icon>
Command Palette Demo
</h2>
<p class="demo-description">
A powerful command palette for quick navigation and actions. Press <kbd>Ctrl+K</kbd> (or <kbd>⌘K</kbd> on Mac) to open.
</p>
</div>
<!-- Quick Start Section -->
<section class="demo-section">
<h3 class="demo-section-title">Quick Start</h3>
<div class="demo-actions">
<button
class="demo-button demo-button--primary"
(click)="openCommandPalette()"
>
<fa-icon [icon]="faKeyboard"></fa-icon>
Open Command Palette
</button>
<button
class="demo-button demo-button--secondary"
(click)="registerSampleCommands()"
>
<fa-icon [icon]="faPlus"></fa-icon>
Add Sample Commands
</button>
<button
class="demo-button demo-button--outline"
(click)="clearCommands()"
>
Clear Commands
</button>
</div>
</section>
<!-- Configuration Section -->
<section class="demo-section">
<h3 class="demo-section-title">Configuration</h3>
<div class="demo-config-grid">
<div class="demo-config-item">
<label class="demo-label">
<input
type="checkbox"
[checked]="config.showCategories"
(change)="updateConfig('showCategories', $event)"
class="demo-checkbox"
/>
Show Categories
</label>
</div>
<div class="demo-config-item">
<label class="demo-label">
<input
type="checkbox"
[checked]="config.showShortcuts"
(change)="updateConfig('showShortcuts', $event)"
class="demo-checkbox"
/>
Show Shortcuts
</label>
</div>
<div class="demo-config-item">
<label class="demo-label">
<input
type="checkbox"
[checked]="config.showRecent"
(change)="updateConfig('showRecent', $event)"
class="demo-checkbox"
/>
Show Recent Commands
</label>
</div>
<div class="demo-config-item">
<label class="demo-label">
Max Results:
<input
type="number"
[value]="config.maxResults"
(input)="updateConfig('maxResults', $event)"
class="demo-input"
min="1"
max="50"
/>
</label>
</div>
<div class="demo-config-item">
<label class="demo-label">
Recent Limit:
<input
type="number"
[value]="config.recentLimit"
(input)="updateConfig('recentLimit', $event)"
class="demo-input"
min="1"
max="20"
/>
</label>
</div>
<div class="demo-config-item">
<label class="demo-label">
Size:
<select
[value]="paletteSize"
(change)="updatePaletteSize($event)"
class="demo-select"
>
<option value="md">Medium</option>
<option value="lg">Large</option>
<option value="xl">Extra Large</option>
</select>
</label>
</div>
</div>
</section>
<!-- Features Section -->
<section class="demo-section">
<h3 class="demo-section-title">Features</h3>
<div class="demo-features">
<div class="demo-feature">
<fa-icon [icon]="faKeyboard" class="demo-feature-icon"></fa-icon>
<h4>Keyboard Navigation</h4>
<p>Full keyboard support with arrow keys, Enter, and Escape</p>
</div>
<div class="demo-feature">
<fa-icon [icon]="faSearch" class="demo-feature-icon"></fa-icon>
<h4>Fuzzy Search</h4>
<p>Intelligent search with typo tolerance and keyword matching</p>
</div>
<div class="demo-feature">
<fa-icon [icon]="faRocket" class="demo-feature-icon"></fa-icon>
<h4>Quick Actions</h4>
<p>Execute commands instantly with global keyboard shortcuts</p>
</div>
<div class="demo-feature">
<fa-icon [icon]="faCog" class="demo-feature-icon"></fa-icon>
<h4>Categorization</h4>
<p>Organize commands by category for better discoverability</p>
</div>
<div class="demo-feature">
<fa-icon [icon]="faCopy" class="demo-feature-icon"></fa-icon>
<h4>Recent Commands</h4>
<p>Track and show recently used commands for quick access</p>
</div>
<div class="demo-feature">
<fa-icon [icon]="faEye" class="demo-feature-icon"></fa-icon>
<h4>Context Aware</h4>
<p>Commands can be shown or hidden based on application state</p>
</div>
</div>
</section>
<!-- Usage Statistics -->
<section class="demo-section">
<h3 class="demo-section-title">Usage Statistics</h3>
<div class="demo-stats">
<div class="demo-stat">
<div class="demo-stat-value">{{ registeredCommandsCount }}</div>
<div class="demo-stat-label">Registered Commands</div>
</div>
<div class="demo-stat">
<div class="demo-stat-value">{{ recentCommandsCount }}</div>
<div class="demo-stat-label">Recent Commands</div>
</div>
<div class="demo-stat">
<div class="demo-stat-value">{{ executionCount }}</div>
<div class="demo-stat-label">Commands Executed</div>
</div>
</div>
</section>
<!-- Keyboard Shortcuts Guide -->
<section class="demo-section">
<h3 class="demo-section-title">Keyboard Shortcuts</h3>
<div class="demo-shortcuts">
<div class="demo-shortcut">
<kbd class="demo-kbd">Ctrl</kbd> + <kbd class="demo-kbd">K</kbd>
<span>Open Command Palette</span>
</div>
<div class="demo-shortcut">
<kbd class="demo-kbd">↑</kbd> / <kbd class="demo-kbd">↓</kbd>
<span>Navigate Results</span>
</div>
<div class="demo-shortcut">
<kbd class="demo-kbd">Enter</kbd>
<span>Execute Selected Command</span>
</div>
<div class="demo-shortcut">
<kbd class="demo-kbd">Esc</kbd>
<span>Close Palette</span>
</div>
<div class="demo-shortcut">
<kbd class="demo-kbd">Tab</kbd>
<span>Navigate Results (Alternative)</span>
</div>
<div class="demo-shortcut">
<kbd class="demo-kbd">/</kbd>
<span>Quick Search</span>
</div>
</div>
</section>
<!-- Command Palette Component -->
<ui-command-palette
#commandPalette
[size]="paletteSize"
[showFooter]="true"
[autoFocus]="true"
(opened)="onPaletteOpened()"
(closed)="onPaletteClosed()"
(commandExecuted)="onCommandExecuted($event)"
/>
</div>
`,
styleUrl: './command-palette-demo.component.scss'
})
export class CommandPaletteDemoComponent implements OnInit, OnDestroy {
@ViewChild('commandPalette') commandPalette!: CommandPaletteComponent;
private router = inject(Router);
private commandService = inject(CommandPaletteService);
// Icons
readonly faPalette = faPalette;
readonly faKeyboard = faKeyboard;
readonly faPlus = faPlus;
readonly faSearch = faSearch;
readonly faRocket = faRocket;
readonly faCog = faCog;
readonly faCopy = faCopy;
readonly faEye = faEye;
readonly faHome = faHome;
readonly faUser = faUser;
readonly faFile = faFile;
readonly faEdit = faEdit;
readonly faTools = faTools;
readonly faQuestion = faQuestion;
readonly faSave = faSave;
readonly faUndo = faUndo;
readonly faRedo = faRedo;
readonly faPaste = faPaste;
// Component state
paletteSize: 'md' | 'lg' | 'xl' = 'lg';
executionCount = 0;
config = {
showCategories: true,
showShortcuts: true,
showRecent: true,
maxResults: 20,
recentLimit: 5
};
globalShortcuts = [
createShortcuts().commandPalette(),
createShortcuts().quickSearch(),
createShortcuts().create('/', { preventDefault: true })
];
get registeredCommandsCount(): number {
return this.commandService.commands().length;
}
get recentCommandsCount(): number {
return this.commandService.recentCommands().length;
}
ngOnInit(): void {
this.registerSampleCommands();
this.applyConfig();
}
ngOnDestroy(): void {
// Clean up registered commands
this.commandService.clearCommands();
}
openCommandPalette(): void {
this.commandPalette.open();
}
handleGlobalShortcut(event: any): void {
const shortcut = event.shortcut;
if (shortcut.key === 'k' && (shortcut.ctrlKey || shortcut.metaKey)) {
this.commandPalette.toggle();
} else if (shortcut.key === '/') {
this.commandPalette.open();
}
}
registerSampleCommands(): void {
const sampleCommands: Command[] = [
// Navigation Commands
{
id: 'nav-home',
title: 'Go Home',
description: 'Navigate to the home page',
icon: faHome,
category: CommandCategory.NAVIGATION,
keywords: ['home', 'dashboard', 'main'],
shortcut: ['Ctrl', 'H'],
handler: () => this.showToast('Navigating to home...'),
order: 1
},
{
id: 'nav-profile',
title: 'View Profile',
description: 'Go to user profile page',
icon: faUser,
category: CommandCategory.NAVIGATION,
keywords: ['profile', 'user', 'account'],
handler: () => this.showToast('Opening profile...'),
order: 2
},
{
id: 'nav-settings',
title: 'Open Settings',
description: 'Access application settings',
icon: faCog,
category: CommandCategory.SETTINGS,
keywords: ['settings', 'preferences', 'config'],
shortcut: ['Ctrl', ','],
handler: () => this.showToast('Opening settings...'),
order: 1
},
// File Commands
{
id: 'file-new',
title: 'New File',
description: 'Create a new file',
icon: faFile,
category: CommandCategory.FILE,
keywords: ['new', 'create', 'file', 'document'],
shortcut: ['Ctrl', 'N'],
handler: () => this.showToast('Creating new file...'),
order: 1
},
{
id: 'file-save',
title: 'Save File',
description: 'Save the current file',
icon: faSave,
category: CommandCategory.FILE,
keywords: ['save', 'file', 'document'],
shortcut: ['Ctrl', 'S'],
handler: () => this.showToast('Saving file...'),
order: 2
},
// Edit Commands
{
id: 'edit-copy',
title: 'Copy',
description: 'Copy selected content',
icon: faCopy,
category: CommandCategory.EDIT,
keywords: ['copy', 'clipboard'],
shortcut: ['Ctrl', 'C'],
handler: () => this.showToast('Content copied!'),
order: 1
},
{
id: 'edit-paste',
title: 'Paste',
description: 'Paste from clipboard',
icon: faPaste,
category: CommandCategory.EDIT,
keywords: ['paste', 'clipboard'],
shortcut: ['Ctrl', 'V'],
handler: () => this.showToast('Content pasted!'),
order: 2
},
{
id: 'edit-undo',
title: 'Undo',
description: 'Undo last action',
icon: faUndo,
category: CommandCategory.EDIT,
keywords: ['undo', 'revert'],
shortcut: ['Ctrl', 'Z'],
handler: () => this.showToast('Action undone!'),
order: 3
},
{
id: 'edit-redo',
title: 'Redo',
description: 'Redo last undone action',
icon: faRedo,
category: CommandCategory.EDIT,
keywords: ['redo', 'repeat'],
shortcut: ['Ctrl', 'Y'],
handler: () => this.showToast('Action redone!'),
order: 4
},
// Search Commands
{
id: 'search-global',
title: 'Global Search',
description: 'Search across all content',
icon: faSearch,
category: CommandCategory.SEARCH,
keywords: ['search', 'find', 'global'],
shortcut: ['Ctrl', 'Shift', 'F'],
handler: () => this.showToast('Opening global search...'),
order: 1
},
// Tools Commands
{
id: 'tools-theme',
title: 'Toggle Theme',
description: 'Switch between light and dark theme',
icon: faTools,
category: CommandCategory.TOOLS,
keywords: ['theme', 'dark', 'light', 'toggle'],
handler: () => this.showToast('Theme toggled!'),
order: 1
},
// Help Commands
{
id: 'help-docs',
title: 'Documentation',
description: 'Open help documentation',
icon: faQuestion,
category: CommandCategory.HELP,
keywords: ['help', 'docs', 'documentation'],
handler: () => this.showToast('Opening documentation...'),
order: 1
},
// Action Commands
{
id: 'action-refresh',
title: 'Refresh Page',
description: 'Reload the current page',
icon: faRocket,
category: CommandCategory.ACTIONS,
keywords: ['refresh', 'reload', 'update'],
shortcut: ['F5'],
handler: () => this.showToast('Page refreshed!'),
order: 1
}
];
this.commandService.registerCommands(sampleCommands);
}
clearCommands(): void {
this.commandService.clearCommands();
this.commandService.clearRecentCommands();
}
updateConfig(key: string, event: Event): void {
const target = event.target as HTMLInputElement;
let value: any = target.type === 'checkbox' ? target.checked :
target.type === 'number' ? parseInt(target.value, 10) :
target.value;
(this.config as any)[key] = value;
this.applyConfig();
}
updatePaletteSize(event: Event): void {
const target = event.target as HTMLSelectElement;
this.paletteSize = target.value as 'md' | 'lg' | 'xl';
}
private applyConfig(): void {
this.commandService.updateConfig({
showCategories: this.config.showCategories,
showShortcuts: this.config.showShortcuts,
showRecent: this.config.showRecent,
maxResults: this.config.maxResults,
recentLimit: this.config.recentLimit
});
}
onPaletteOpened(): void {
console.log('Command palette opened');
}
onPaletteClosed(): void {
console.log('Command palette closed');
}
onCommandExecuted(event: { commandId: string; context: CommandExecutionContext }): void {
this.executionCount++;
console.log('Command executed:', event);
}
private showToast(message: string): void {
// Simple toast implementation for demo
const toast = document.createElement('div');
toast.className = 'demo-toast';
toast.textContent = message;
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #333;
color: white;
padding: 12px 24px;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
z-index: 10000;
font-size: 14px;
animation: slideIn 0.3s ease;
`;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.animation = 'slideOut 0.3s ease forwards';
setTimeout(() => document.body.removeChild(toast), 300);
}, 3000);
}
}