Files
ui-essentials/projects/demo-ui-essentials/src/app/demos/search-demo/search-demo.component.ts
skyai_dev 5983722793 Add comprehensive component library and demo application
Added extensive component library with feedback components (empty state, loading spinner, skeleton loader), enhanced form components (autocomplete, date picker, file upload, form field, time picker), navigation components (pagination), and overlay components (backdrop, drawer, modal, overlay container). Updated demo application with comprehensive showcase components and enhanced styling throughout the project. Excluded font files from repository to reduce size.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-03 05:38:09 +10:00

550 lines
18 KiB
TypeScript

import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { SearchBarComponent, SearchSuggestion } from '../../../../../ui-essentials/src/lib/components/forms/search';
import {
faSearch,
faMicrophone,
faCamera,
faFilter,
faUser,
faFile,
faFolder,
faTag,
faHeart,
faStar
} from '@fortawesome/free-solid-svg-icons';
@Component({
selector: 'ui-search-demo',
standalone: true,
imports: [
CommonModule,
FormsModule,
SearchBarComponent
],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div style="padding: 2rem;">
<h2>Search Bar Component Showcase</h2>
<!-- Basic Search Bars -->
<section style="margin-bottom: 3rem;">
<h3>Basic Search Bars</h3>
<div style="display: flex; flex-direction: column; gap: 1rem; margin-bottom: 2rem;">
<ui-search-bar
placeholder="Search anything..."
[(ngModel)]="basicSearch1"
(searchChange)="onSearchChange('basic-1', $event)"
(searchSubmit)="onSearchSubmit('basic-1', $event)"
/>
<ui-search-bar
placeholder="Search with label..."
label="Search"
[(ngModel)]="basicSearch2"
(searchChange)="onSearchChange('basic-2', $event)"
/>
<ui-search-bar
placeholder="Required search field..."
label="Search (Required)"
[required]="true"
[(ngModel)]="basicSearch3"
(searchChange)="onSearchChange('basic-3', $event)"
/>
</div>
</section>
<!-- Size Variants -->
<section style="margin-bottom: 3rem;">
<h3>Size Variants</h3>
<div style="display: flex; flex-direction: column; gap: 1rem; margin-bottom: 2rem;">
<ui-search-bar
placeholder="Small search..."
size="sm"
[(ngModel)]="sizeSmall"
(searchChange)="onSearchChange('size-sm', $event)"
/>
<ui-search-bar
placeholder="Medium search (default)..."
size="md"
[(ngModel)]="sizeMedium"
(searchChange)="onSearchChange('size-md', $event)"
/>
<ui-search-bar
placeholder="Large search..."
size="lg"
[(ngModel)]="sizeLarge"
(searchChange)="onSearchChange('size-lg', $event)"
/>
</div>
</section>
<!-- Variant Styles -->
<section style="margin-bottom: 3rem;">
<h3>Style Variants</h3>
<div style="display: flex; flex-direction: column; gap: 1rem; margin-bottom: 2rem;">
<ui-search-bar
placeholder="Outlined search (default)..."
variant="outlined"
[(ngModel)]="variantOutlined"
(searchChange)="onSearchChange('variant-outlined', $event)"
/>
<ui-search-bar
placeholder="Filled search..."
variant="filled"
[(ngModel)]="variantFilled"
(searchChange)="onSearchChange('variant-filled', $event)"
/>
<ui-search-bar
placeholder="Elevated search..."
variant="elevated"
[(ngModel)]="variantElevated"
(searchChange)="onSearchChange('variant-elevated', $event)"
/>
</div>
</section>
<!-- With Icons -->
<section style="margin-bottom: 3rem;">
<h3>With Icons</h3>
<div style="display: flex; flex-direction: column; gap: 1rem; margin-bottom: 2rem;">
<ui-search-bar
placeholder="Custom leading icon..."
[leadingIcon]="faUser"
[(ngModel)]="iconSearch1"
(searchChange)="onSearchChange('icon-1', $event)"
/>
<ui-search-bar
placeholder="With trailing icons..."
[trailingIcons]="basicTrailingIcons"
[(ngModel)]="iconSearch2"
(searchChange)="onSearchChange('icon-2', $event)"
(trailingIconClick)="onTrailingIconClick($event)"
/>
<ui-search-bar
placeholder="Multiple trailing icons..."
[trailingIcons]="multipleTrailingIcons"
[(ngModel)]="iconSearch3"
(searchChange)="onSearchChange('icon-3', $event)"
(trailingIconClick)="onTrailingIconClick($event)"
/>
</div>
</section>
<!-- With Suggestions -->
<section style="margin-bottom: 3rem;">
<h3>With Suggestions</h3>
<div style="display: flex; flex-direction: column; gap: 1rem; margin-bottom: 2rem;">
<ui-search-bar
placeholder="Search with suggestions..."
[suggestions]="suggestions"
[(ngModel)]="suggestionSearch1"
(searchChange)="onSearchChange('suggestions-1', $event)"
(suggestionSelect)="onSuggestionSelect($event)"
/>
<ui-search-bar
placeholder="Search with categorized suggestions..."
[suggestions]="categorizedSuggestions"
[(ngModel)]="suggestionSearch2"
(searchChange)="onSearchChange('suggestions-2', $event)"
(suggestionSelect)="onSuggestionSelect($event)"
/>
</div>
</section>
<!-- States -->
<section style="margin-bottom: 3rem;">
<h3>States</h3>
<div style="display: flex; flex-direction: column; gap: 1rem; margin-bottom: 2rem;">
<ui-search-bar
placeholder="Disabled search..."
[disabled]="true"
[(ngModel)]="stateDisabled"
/>
<ui-search-bar
placeholder="Readonly search..."
[readonly]="true"
[(ngModel)]="stateReadonly"
/>
<ui-search-bar
placeholder="Error state..."
state="error"
errorMessage="Please enter a valid search term"
[(ngModel)]="stateError"
(searchChange)="onSearchChange('state-error', $event)"
/>
<ui-search-bar
placeholder="With helper text..."
helperText="Enter at least 3 characters to search"
[(ngModel)]="stateHelper"
(searchChange)="onSearchChange('state-helper', $event)"
/>
</div>
</section>
<!-- Advanced Features -->
<section style="margin-bottom: 3rem;">
<h3>Advanced Features</h3>
<div style="display: flex; flex-direction: column; gap: 1rem; margin-bottom: 2rem;">
<ui-search-bar
placeholder="Non-clearable search..."
[clearable]="false"
[(ngModel)]="advancedSearch1"
(searchChange)="onSearchChange('advanced-1', $event)"
/>
<ui-search-bar
placeholder="Auto-focus search..."
[autofocus]="true"
[(ngModel)]="advancedSearch2"
(searchChange)="onSearchChange('advanced-2', $event)"
/>
<ui-search-bar
placeholder="Search with max suggestions..."
[suggestions]="suggestions"
[maxSuggestions]="3"
[(ngModel)]="advancedSearch3"
(searchChange)="onSearchChange('advanced-3', $event)"
/>
</div>
</section>
<!-- Interactive Controls -->
<section style="margin-bottom: 3rem;">
<h3>Interactive Controls</h3>
<div style="display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem;">
<button
style="padding: 0.5rem 1rem; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;"
(click)="clearAllSearches()"
>
Clear All Searches
</button>
<button
style="padding: 0.5rem 1rem; background: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer;"
(click)="fillSampleSearches()"
>
Fill Sample Searches
</button>
<button
style="padding: 0.5rem 1rem; background: #6f42c1; color: white; border: none; border-radius: 4px; cursor: pointer;"
(click)="toggleStates()"
>
{{ globalDisabled() ? 'Enable All' : 'Disable All' }}
</button>
</div>
@if (lastAction()) {
<div style="margin-top: 1rem; padding: 1rem; background: #e3f2fd; border-radius: 4px;">
<strong>Last action:</strong> {{ lastAction() }}
</div>
}
</section>
<!-- Code Examples -->
<section style="margin-bottom: 3rem;">
<h3>Code Examples</h3>
<div style="background: #f8f9fa; padding: 1rem; border-radius: 4px; margin-bottom: 1rem;">
<h4>Basic Usage:</h4>
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code>&lt;ui-search-bar
placeholder="Search..."
[(ngModel)]="searchValue"
(searchChange)="onSearchChange($event)"
(searchSubmit)="onSearchSubmit($event)"&gt;
&lt;/ui-search-bar&gt;</code></pre>
<h4>With Suggestions:</h4>
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code>&lt;ui-search-bar
placeholder="Search with suggestions..."
[suggestions]="suggestions"
[(ngModel)]="searchValue"
(suggestionSelect)="onSuggestionSelect($event)"&gt;
&lt;/ui-search-bar&gt;</code></pre>
<h4>With Icons:</h4>
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code>&lt;ui-search-bar
placeholder="Search with icons..."
[leadingIcon]="faSearch"
[trailingIcons]="iconButtons"
variant="filled"
size="lg"
(trailingIconClick)="onIconClick($event)"&gt;
&lt;/ui-search-bar&gt;</code></pre>
</div>
</section>
<!-- Current Values -->
<section style="margin-bottom: 3rem;">
<h3>Current Values</h3>
<div style="background: #f8f9fa; padding: 1rem; border-radius: 4px; max-height: 400px; overflow-y: auto;">
<pre>{{ getCurrentValues() }}</pre>
</div>
</section>
</div>
`,
styles: [`
h2 {
color: hsl(279, 14%, 11%);
font-size: 2rem;
margin-bottom: 2rem;
border-bottom: 2px solid hsl(258, 100%, 47%);
padding-bottom: 0.5rem;
}
h3 {
color: hsl(279, 14%, 25%);
font-size: 1.5rem;
margin-bottom: 1rem;
}
h4 {
color: hsl(279, 14%, 35%);
font-size: 1.2rem;
margin-bottom: 0.5rem;
}
pre {
margin: 0;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.4;
}
code {
color: hsl(279, 14%, 15%);
}
section {
border: 1px solid #e9ecef;
padding: 1.5rem;
border-radius: 8px;
background: #ffffff;
}
button:hover {
opacity: 0.9;
transform: translateY(-1px);
}
`]
})
export class SearchDemoComponent {
// FontAwesome icons
protected readonly faSearch = faSearch;
protected readonly faMicrophone = faMicrophone;
protected readonly faCamera = faCamera;
protected readonly faFilter = faFilter;
protected readonly faUser = faUser;
protected readonly faFile = faFile;
protected readonly faFolder = faFolder;
protected readonly faTag = faTag;
protected readonly faHeart = faHeart;
protected readonly faStar = faStar;
// Basic search values
basicSearch1 = signal<string>('');
basicSearch2 = signal<string>('');
basicSearch3 = signal<string>('');
// Size variants
sizeSmall = signal<string>('');
sizeMedium = signal<string>('');
sizeLarge = signal<string>('');
// Variant styles
variantOutlined = signal<string>('');
variantFilled = signal<string>('');
variantElevated = signal<string>('');
// Icon searches
iconSearch1 = signal<string>('');
iconSearch2 = signal<string>('');
iconSearch3 = signal<string>('');
// Suggestion searches
suggestionSearch1 = signal<string>('');
suggestionSearch2 = signal<string>('');
// State searches
stateDisabled = signal<string>('Disabled value');
stateReadonly = signal<string>('Readonly value');
stateError = signal<string>('');
stateHelper = signal<string>('');
// Advanced searches
advancedSearch1 = signal<string>('');
advancedSearch2 = signal<string>('');
advancedSearch3 = signal<string>('');
// Control states
globalDisabled = signal<boolean>(false);
lastAction = signal<string>('');
// Icon configurations
basicTrailingIcons = [
{
id: 'mic',
icon: faMicrophone,
label: 'Voice search',
disabled: false
}
];
multipleTrailingIcons = [
{
id: 'mic',
icon: faMicrophone,
label: 'Voice search',
disabled: false
},
{
id: 'camera',
icon: faCamera,
label: 'Image search',
disabled: false
},
{
id: 'filter',
icon: faFilter,
label: 'Filter results',
disabled: false
}
];
// Search suggestions
suggestions: SearchSuggestion[] = [
{ id: '1', text: 'Angular components', icon: faFile },
{ id: '2', text: 'TypeScript interfaces', icon: faFile },
{ id: '3', text: 'SCSS mixins', icon: faFile },
{ id: '4', text: 'Design tokens', icon: faTag },
{ id: '5', text: 'Material Design', icon: faHeart },
{ id: '6', text: 'Component library', icon: faFolder },
{ id: '7', text: 'UI patterns', icon: faStar },
{ id: '8', text: 'Accessibility guidelines', icon: faFile }
];
categorizedSuggestions: SearchSuggestion[] = [
{ id: '1', text: 'User profile', category: 'Users', icon: faUser },
{ id: '2', text: 'Project documentation', category: 'Files', icon: faFile },
{ id: '3', text: 'Design system', category: 'Files', icon: faFolder },
{ id: '4', text: 'Component library', category: 'Code', icon: faTag },
{ id: '5', text: 'UI components', category: 'Code', icon: faTag },
{ id: '6', text: 'Admin settings', category: 'Settings', icon: faFilter }
];
onSearchChange(searchId: string, value: string): void {
this.lastAction.set(`Search changed: ${searchId} = "${value}"`);
console.log(`Search ${searchId} changed:`, value);
}
onSearchSubmit(searchId: string, value: string): void {
this.lastAction.set(`Search submitted: ${searchId} = "${value}"`);
console.log(`Search ${searchId} submitted:`, value);
}
onSuggestionSelect(suggestion: SearchSuggestion): void {
this.lastAction.set(`Suggestion selected: "${suggestion.text}" (${suggestion.id})`);
console.log('Suggestion selected:', suggestion);
}
onTrailingIconClick(iconData: {id: string, icon: any}): void {
this.lastAction.set(`Icon clicked: ${iconData.id}`);
console.log('Trailing icon clicked:', iconData);
}
clearAllSearches(): void {
this.basicSearch1.set('');
this.basicSearch2.set('');
this.basicSearch3.set('');
this.sizeSmall.set('');
this.sizeMedium.set('');
this.sizeLarge.set('');
this.variantOutlined.set('');
this.variantFilled.set('');
this.variantElevated.set('');
this.iconSearch1.set('');
this.iconSearch2.set('');
this.iconSearch3.set('');
this.suggestionSearch1.set('');
this.suggestionSearch2.set('');
this.stateError.set('');
this.stateHelper.set('');
this.advancedSearch1.set('');
this.advancedSearch2.set('');
this.advancedSearch3.set('');
this.lastAction.set('All searches cleared');
}
fillSampleSearches(): void {
this.basicSearch1.set('Sample search');
this.basicSearch2.set('Labeled search');
this.basicSearch3.set('Required search');
this.sizeSmall.set('Small');
this.sizeMedium.set('Medium');
this.sizeLarge.set('Large');
this.variantOutlined.set('Outlined');
this.variantFilled.set('Filled');
this.variantElevated.set('Elevated');
this.iconSearch1.set('User search');
this.iconSearch2.set('Voice search');
this.iconSearch3.set('Multi-icon search');
this.suggestionSearch1.set('Angular');
this.suggestionSearch2.set('Design');
this.stateError.set('Error text');
this.stateHelper.set('Helper text');
this.advancedSearch1.set('Advanced');
this.advancedSearch2.set('Focused');
this.advancedSearch3.set('Limited');
this.lastAction.set('All searches filled with sample data');
}
toggleStates(): void {
this.globalDisabled.update(disabled => !disabled);
this.lastAction.set(`Global state: ${this.globalDisabled() ? 'disabled' : 'enabled'}`);
}
getCurrentValues(): string {
const values = {
basic: {
search1: this.basicSearch1(),
search2: this.basicSearch2(),
search3: this.basicSearch3()
},
sizes: {
small: this.sizeSmall(),
medium: this.sizeMedium(),
large: this.sizeLarge()
},
variants: {
outlined: this.variantOutlined(),
filled: this.variantFilled(),
elevated: this.variantElevated()
},
icons: {
icon1: this.iconSearch1(),
icon2: this.iconSearch2(),
icon3: this.iconSearch3()
},
suggestions: {
suggestion1: this.suggestionSearch1(),
suggestion2: this.suggestionSearch2()
},
states: {
disabled: this.stateDisabled(),
readonly: this.stateReadonly(),
error: this.stateError(),
helper: this.stateHelper()
},
advanced: {
advanced1: this.advancedSearch1(),
advanced2: this.advancedSearch2(),
advanced3: this.advancedSearch3()
},
controls: {
globalDisabled: this.globalDisabled(),
lastAction: this.lastAction()
}
};
return JSON.stringify(values, null, 2);
}
}