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>
This commit is contained in:
skyai_dev
2025-09-03 05:38:09 +10:00
parent c803831f60
commit 5983722793
246 changed files with 52845 additions and 25 deletions

View File

@@ -0,0 +1,669 @@
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormControl, FormGroup, ReactiveFormsModule, Validators, AbstractControl, ValidationErrors } from '@angular/forms';
import { FormFieldComponent } from '../../../../../ui-essentials/src/lib/components/forms/form-field';
@Component({
selector: 'ui-form-field-demo',
standalone: true,
imports: [CommonModule, ReactiveFormsModule, FormFieldComponent],
template: `
<div class="demo-container">
<h2>Form Field Demo</h2>
<!-- Size Variants -->
<section class="demo-section">
<h3>Sizes</h3>
<div class="demo-grid">
@for (size of sizes; track size) {
<div class="demo-item">
<h4>{{ size.toUpperCase() }} Size</h4>
<p>Form field with {{ size }} sizing.</p>
<ui-form-field
[size]="size"
label="Label ({{ size }})"
helperText="This is helper text"
>
<input
class="demo-input"
type="text"
placeholder="Enter text..."
/>
</ui-form-field>
</div>
}
</div>
</section>
<!-- Layout Variants -->
<section class="demo-section">
<h3>Layout Options</h3>
<div class="layout-demo">
<div class="demo-item">
<h4>Vertical Layout (Default)</h4>
<p>Standard vertical layout with label above the control.</p>
<ui-form-field
label="Vertical Label"
helperText="Helper text appears below the control"
layout="vertical"
>
<input
class="demo-input"
type="text"
placeholder="Enter text..."
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Horizontal Layout</h4>
<p>Horizontal layout with label beside the control.</p>
<ui-form-field
label="Horizontal Label"
helperText="Helper text appears below the control"
layout="horizontal"
>
<input
class="demo-input"
type="text"
placeholder="Enter text..."
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Compact Layout</h4>
<p>Compact spacing for tighter layouts.</p>
<ui-form-field
label="Compact Label"
helperText="Reduced spacing"
[compact]="true"
>
<input
class="demo-input"
type="text"
placeholder="Enter text..."
/>
</ui-form-field>
</div>
</div>
</section>
<!-- State Variants -->
<section class="demo-section">
<h3>States</h3>
<div class="demo-grid">
<div class="demo-item">
<h4>Default State</h4>
<p>Normal state with no validation.</p>
<ui-form-field
label="Default Field"
helperText="This is in default state"
>
<input
class="demo-input"
type="text"
placeholder="Enter text..."
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Error State</h4>
<p>Error state with error message.</p>
<ui-form-field
label="Error Field"
state="error"
errorMessage="This field has an error"
>
<input
class="demo-input demo-input--error"
type="text"
placeholder="Enter text..."
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Success State</h4>
<p>Success state with success message.</p>
<ui-form-field
label="Success Field"
state="success"
successMessage="This field is valid"
>
<input
class="demo-input demo-input--success"
type="text"
value="Valid input"
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Warning State</h4>
<p>Warning state with warning message.</p>
<ui-form-field
label="Warning Field"
state="warning"
warningMessage="This is a warning message"
>
<input
class="demo-input"
type="text"
placeholder="Enter text..."
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Info State</h4>
<p>Info state with informational message.</p>
<ui-form-field
label="Info Field"
state="info"
infoMessage="This is informational text"
>
<input
class="demo-input"
type="text"
placeholder="Enter text..."
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Disabled State</h4>
<p>Disabled field with disabled styling.</p>
<ui-form-field
label="Disabled Field"
helperText="This field is disabled"
[disabled]="true"
>
<input
class="demo-input"
type="text"
placeholder="Disabled input"
disabled
/>
</ui-form-field>
</div>
</div>
</section>
<!-- Required and Optional -->
<section class="demo-section">
<h3>Required and Optional Fields</h3>
<div class="demo-grid">
<div class="demo-item">
<h4>Required Field</h4>
<p>Field marked as required with asterisk.</p>
<ui-form-field
label="Required Field"
[required]="true"
helperText="This field is required"
>
<input
class="demo-input"
type="text"
placeholder="Required input"
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Optional Field</h4>
<p>Field marked as optional with (optional) text.</p>
<ui-form-field
label="Optional Field"
[showOptional]="true"
helperText="This field is optional"
>
<input
class="demo-input"
type="text"
placeholder="Optional input"
/>
</ui-form-field>
</div>
</div>
</section>
<!-- Different Input Types -->
<section class="demo-section">
<h3>Input Types</h3>
<div class="demo-grid">
<div class="demo-item">
<h4>Text Input</h4>
<ui-form-field
label="Text Input"
helperText="Enter any text"
>
<input
class="demo-input"
type="text"
placeholder="Text input"
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Email Input</h4>
<ui-form-field
label="Email Input"
helperText="Enter a valid email address"
>
<input
class="demo-input"
type="email"
placeholder="email@example.com"
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Number Input</h4>
<ui-form-field
label="Number Input"
helperText="Enter a number"
>
<input
class="demo-input"
type="number"
placeholder="123"
/>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Textarea</h4>
<ui-form-field
label="Textarea"
helperText="Enter multiple lines of text"
>
<textarea
class="demo-textarea"
placeholder="Multi-line text input"
></textarea>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Select</h4>
<ui-form-field
label="Select"
helperText="Choose an option"
>
<select class="demo-select">
<option value="">Choose an option</option>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
</ui-form-field>
</div>
<div class="demo-item">
<h4>Checkbox Group</h4>
<ui-form-field
label="Preferences"
helperText="Select your preferences"
[group]="true"
>
<div class="demo-checkbox">
<input type="checkbox" id="pref1" />
<label for="pref1">Option 1</label>
</div>
<div class="demo-checkbox">
<input type="checkbox" id="pref2" />
<label for="pref2">Option 2</label>
</div>
<div class="demo-checkbox">
<input type="checkbox" id="pref3" />
<label for="pref3">Option 3</label>
</div>
</ui-form-field>
</div>
</div>
</section>
<!-- Character Count -->
<section class="demo-section">
<h3>Character Count</h3>
<div class="demo-grid">
<div class="demo-item">
<h4>With Character Counter</h4>
<p>Shows character count with maximum limit.</p>
<ui-form-field
label="Limited Text"
helperText="Maximum 50 characters"
[showCharacterCount]="true"
[maxLength]="50"
[currentLength]="characterCountText.length"
>
<input
class="demo-input"
type="text"
placeholder="Type here..."
[value]="characterCountText"
(input)="updateCharacterCount($event)"
maxlength="50"
/>
</ui-form-field>
</div>
</div>
</section>
<!-- Hint Text -->
<section class="demo-section">
<h3>Hint Text</h3>
<div class="demo-grid">
<div class="demo-item">
<h4>With Hint</h4>
<p>Additional helpful information with icon.</p>
<ui-form-field
label="Password"
helperText="Enter a secure password"
hintText="Password should be at least 8 characters long and contain uppercase, lowercase, numbers, and special characters"
>
<input
class="demo-input"
type="password"
placeholder="Enter password"
/>
</ui-form-field>
</div>
</div>
</section>
<!-- Reactive Form Integration -->
<section class="demo-section">
<h3>Reactive Form Integration</h3>
<div class="demo-item">
<h4>Form with Validation</h4>
<p>Form fields with Angular reactive form validation.</p>
<form [formGroup]="validationForm" class="form-demo">
<!-- Required Field -->
<ui-form-field
label="Full Name"
[required]="true"
helperText="Enter your full name"
[validationMessages]="validationMessages"
>
<input
class="demo-input"
type="text"
placeholder="John Doe"
formControlName="name"
[class.demo-input--error]="isFieldInvalid('name')"
[class.demo-input--success]="isFieldValid('name')"
/>
</ui-form-field>
<!-- Email with validation -->
<ui-form-field
label="Email Address"
[required]="true"
helperText="We'll never share your email"
[validationMessages]="validationMessages"
>
<input
class="demo-input"
type="email"
placeholder="john@example.com"
formControlName="email"
[class.demo-input--error]="isFieldInvalid('email')"
[class.demo-input--success]="isFieldValid('email')"
/>
</ui-form-field>
<!-- Password with length validation -->
<ui-form-field
label="Password"
[required]="true"
helperText="Must be at least 8 characters"
[validationMessages]="validationMessages"
[showCharacterCount]="true"
[maxLength]="50"
[currentLength]="getFieldValue('password')?.length || 0"
>
<input
class="demo-input"
type="password"
placeholder="Enter password"
formControlName="password"
[class.demo-input--error]="isFieldInvalid('password')"
[class.demo-input--success]="isFieldValid('password')"
/>
</ui-form-field>
<!-- Age with number validation -->
<ui-form-field
label="Age"
[showOptional]="true"
helperText="Must be between 18 and 100"
[validationMessages]="validationMessages"
>
<input
class="demo-input"
type="number"
placeholder="25"
formControlName="age"
[class.demo-input--error]="isFieldInvalid('age')"
[class.demo-input--success]="isFieldValid('age')"
/>
</ui-form-field>
<!-- Website with URL validation -->
<ui-form-field
label="Website"
[showOptional]="true"
helperText="Enter a valid URL"
hintText="Include http:// or https://"
[validationMessages]="validationMessages"
>
<input
class="demo-input"
type="url"
placeholder="https://example.com"
formControlName="website"
[class.demo-input--error]="isFieldInvalid('website')"
[class.demo-input--success]="isFieldValid('website')"
/>
</ui-form-field>
<!-- Bio with custom validation -->
<ui-form-field
label="Bio"
[showOptional]="true"
helperText="Tell us about yourself"
[showCharacterCount]="true"
[maxLength]="200"
[currentLength]="getFieldValue('bio')?.length || 0"
[validationMessages]="validationMessages"
>
<textarea
class="demo-textarea"
placeholder="Write a short bio..."
formControlName="bio"
[class.demo-input--error]="isFieldInvalid('bio')"
[class.demo-input--success]="isFieldValid('bio')"
maxlength="200"
></textarea>
</ui-form-field>
</form>
<div class="demo-actions">
<button
type="button"
class="demo-button"
(click)="markAllTouched()"
>
Validate All
</button>
<button
type="button"
class="demo-button demo-button--secondary"
(click)="resetValidationForm()"
>
Reset Form
</button>
<button
type="button"
class="demo-button demo-button--secondary"
(click)="fillSampleData()"
>
Fill Sample Data
</button>
</div>
<div class="form-status">
<div><strong>Form Status:</strong> {{ validationForm.status }}</div>
<div><strong>Form Valid:</strong> {{ validationForm.valid }}</div>
<div><strong>Form Touched:</strong> {{ validationForm.touched }}</div>
<div><strong>Form Dirty:</strong> {{ validationForm.dirty }}</div>
</div>
</div>
</section>
<!-- Code Example -->
<section class="demo-section">
<h3>Usage Example</h3>
<div class="code-demo">
<pre><code>{{ codeExample }}</code></pre>
</div>
</section>
</div>
`,
styleUrl: './form-field-demo.component.scss'
})
export class FormFieldDemoComponent implements OnInit {
sizes = ['sm', 'md', 'lg'] as const;
characterCountText = '';
validationForm = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(2)]),
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', [Validators.required, Validators.minLength(8)]),
age: new FormControl('', [Validators.min(18), Validators.max(100)]),
website: new FormControl('', [this.urlValidator]),
bio: new FormControl('', [Validators.maxLength(200)])
});
validationMessages = {
required: 'This field is required',
email: 'Please enter a valid email address',
minlength: 'This field is too short',
maxlength: 'This field is too long',
min: 'Value is too low',
max: 'Value is too high',
url: 'Please enter a valid URL',
custom: 'Custom validation error'
};
readonly codeExample = `import { FormFieldComponent } from 'ui-essentials';
// Basic usage
<ui-form-field
label="Email Address"
helperText="We'll never share your email"
[required]="true"
>
<input type="email" formControlName="email" />
</ui-form-field>
// With validation messages
<ui-form-field
label="Password"
[required]="true"
helperText="Must be at least 8 characters"
[validationMessages]="validationMessages"
[showCharacterCount]="true"
[maxLength]="50"
>
<input type="password" formControlName="password" />
</ui-form-field>
// Horizontal layout
<ui-form-field
label="Settings"
layout="horizontal"
helperText="Choose your preferences"
>
<div class="checkbox-group">
<input type="checkbox" id="option1" />
<label for="option1">Option 1</label>
</div>
</ui-form-field>
// Custom states
<ui-form-field
label="Status Field"
state="success"
successMessage="Data saved successfully"
>
<input type="text" readonly value="Saved" />
</ui-form-field>`;
ngOnInit(): void {
// Watch form changes for demo purposes
this.validationForm.valueChanges.subscribe(() => {
// Update demo display if needed
});
}
updateCharacterCount(event: Event): void {
const input = event.target as HTMLInputElement;
this.characterCountText = input.value;
}
isFieldInvalid(fieldName: string): boolean {
const field = this.validationForm.get(fieldName);
return field ? field.invalid && (field.dirty || field.touched) : false;
}
isFieldValid(fieldName: string): boolean {
const field = this.validationForm.get(fieldName);
return field ? field.valid && field.value && (field.dirty || field.touched) : false;
}
getFieldValue(fieldName: string): any {
return this.validationForm.get(fieldName)?.value;
}
markAllTouched(): void {
this.validationForm.markAllAsTouched();
}
resetValidationForm(): void {
this.validationForm.reset();
Object.keys(this.validationForm.controls).forEach(key => {
this.validationForm.get(key)?.setErrors(null);
});
}
fillSampleData(): void {
this.validationForm.patchValue({
name: 'John Doe',
email: 'john.doe@example.com',
password: 'SecurePass123!',
age: "30",
website: 'https://johndoe.com',
bio: 'Software developer with 10 years of experience in web development.'
});
this.validationForm.markAllAsTouched();
}
// Custom URL validator
private urlValidator(control: AbstractControl): ValidationErrors | null {
if (!control.value) return null;
try {
new URL(control.value);
return null;
} catch {
return { url: true };
}
}
}