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:
@@ -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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user