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,152 @@
.demo-container {
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
h2 {
margin-bottom: 1.5rem;
font-size: 1.875rem;
}
}
.demo-section {
margin-bottom: 3rem;
h3 {
margin-bottom: 1rem;
font-size: 1.25rem;
border-bottom: 1px solid #e5e7eb;
padding-bottom: 0.5rem;
}
}
.demo-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
.demo-item {
h4 {
margin-bottom: 0.75rem;
font-size: 1rem;
font-weight: 500;
color: #6b7280;
}
}
.demo-interactive {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
padding: 1.5rem;
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
background: #f9fafb;
}
.demo-output {
h4 {
margin-bottom: 0.5rem;
font-size: 1rem;
}
p {
color: #6b7280;
margin-bottom: 0.25rem;
font-size: 0.875rem;
}
}
.demo-form {
padding: 1.5rem;
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
background: #ffffff;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.form-actions {
display: flex;
gap: 0.75rem;
margin-bottom: 1.5rem;
}
.submit-btn, .reset-btn {
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.submit-btn {
background: #3b82f6;
color: white;
border: 1px solid #3b82f6;
&:hover:not(:disabled) {
background: #2563eb;
border-color: #2563eb;
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
.reset-btn {
background: transparent;
color: #6b7280;
border: 1px solid #d1d5db;
&:hover {
background: #f3f4f6;
color: #374151;
}
}
.form-status {
background: #f3f4f6;
padding: 0.75rem;
border-radius: 0.375rem;
p {
margin-bottom: 0.5rem;
font-size: 0.875rem;
&:last-child {
margin-bottom: 0;
}
}
pre {
background: #e5e7eb;
padding: 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
overflow-x: auto;
}
}
@media (max-width: 768px) {
.demo-row {
grid-template-columns: 1fr;
}
.demo-interactive {
grid-template-columns: 1fr;
}
.form-row {
grid-template-columns: 1fr;
}
}

View File

@@ -0,0 +1,394 @@
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TimePickerComponent, TimeValue } from '../../../../../ui-essentials/src/lib/components/forms/time-picker/time-picker.component';
@Component({
selector: 'ui-time-picker-demo',
standalone: true,
imports: [CommonModule, FormsModule, TimePickerComponent],
template: `
<div class="demo-container">
<h2>Time Picker Demo</h2>
<!-- Size Variants -->
<section class="demo-section">
<h3>Sizes</h3>
<div class="demo-row">
@for (size of sizes; track size) {
<div class="demo-item">
<h4>{{ size | uppercase }}</h4>
<ui-time-picker
[size]="size"
[label]="size + ' Time Picker'"
[placeholder]="'Select time'"
[(ngModel)]="sampleValues[size]">
</ui-time-picker>
</div>
}
</div>
</section>
<!-- Variant Styles -->
<section class="demo-section">
<h3>Variants</h3>
<div class="demo-row">
@for (variant of variants; track variant) {
<div class="demo-item">
<h4>{{ variant | titlecase }}</h4>
<ui-time-picker
[variant]="variant"
[label]="variant + ' Variant'"
[placeholder]="'Select time'"
[(ngModel)]="variantValues[variant]">
</ui-time-picker>
</div>
}
</div>
</section>
<!-- Time Formats -->
<section class="demo-section">
<h3>Time Formats</h3>
<div class="demo-row">
<div class="demo-item">
<h4>12-Hour Format</h4>
<ui-time-picker
label="12-Hour Time"
placeholder="Select time"
timeFormat="12"
helperText="Uses AM/PM format"
[(ngModel)]="formatValues['twelve']">
</ui-time-picker>
</div>
<div class="demo-item">
<h4>24-Hour Format</h4>
<ui-time-picker
label="24-Hour Time"
placeholder="Select time"
timeFormat="24"
helperText="Military time format"
[(ngModel)]="formatValues['twentyFour']">
</ui-time-picker>
</div>
<div class="demo-item">
<h4>With Seconds</h4>
<ui-time-picker
label="Time with Seconds"
placeholder="Select time"
[showSeconds]="true"
helperText="Includes seconds picker"
[(ngModel)]="formatValues['withSeconds']">
</ui-time-picker>
</div>
<div class="demo-item">
<h4>Custom Steps</h4>
<ui-time-picker
label="Custom Minute Steps"
placeholder="Select time"
[minuteStep]="15"
[secondStep]="30"
[showSeconds]="true"
helperText="15-minute and 30-second steps"
[(ngModel)]="formatValues['customSteps']">
</ui-time-picker>
</div>
</div>
</section>
<!-- States -->
<section class="demo-section">
<h3>States</h3>
<div class="demo-row">
<div class="demo-item">
<h4>Default</h4>
<ui-time-picker
label="Default State"
placeholder="Select time"
helperText="Choose your preferred time"
[(ngModel)]="stateValues['default']">
</ui-time-picker>
</div>
<div class="demo-item">
<h4>Error</h4>
<ui-time-picker
label="Error State"
placeholder="Select time"
state="error"
errorMessage="Please select a valid time"
[(ngModel)]="stateValues['error']">
</ui-time-picker>
</div>
<div class="demo-item">
<h4>Success</h4>
<ui-time-picker
label="Success State"
placeholder="Select time"
state="success"
helperText="Perfect timing!"
[(ngModel)]="stateValues['success']">
</ui-time-picker>
</div>
<div class="demo-item">
<h4>Warning</h4>
<ui-time-picker
label="Warning State"
placeholder="Select time"
state="warning"
helperText="Time is outside business hours"
[(ngModel)]="stateValues['warning']">
</ui-time-picker>
</div>
</div>
</section>
<!-- Special Features -->
<section class="demo-section">
<h3>Features</h3>
<div class="demo-row">
<div class="demo-item">
<h4>Required Field</h4>
<ui-time-picker
label="Required Time"
placeholder="Select time"
[required]="true"
helperText="This field is required"
[(ngModel)]="featureValues['required']">
</ui-time-picker>
</div>
<div class="demo-item">
<h4>Disabled</h4>
<ui-time-picker
label="Disabled Time Picker"
placeholder="Cannot select"
[disabled]="true"
helperText="This field is disabled"
[(ngModel)]="featureValues['disabled']">
</ui-time-picker>
</div>
<div class="demo-item">
<h4>Not Clearable</h4>
<ui-time-picker
label="No Clear Button"
placeholder="Select time"
[clearable]="false"
helperText="Clear button is hidden"
[(ngModel)]="featureValues['notClearable']">
</ui-time-picker>
</div>
<div class="demo-item">
<h4>With Presets</h4>
<ui-time-picker
label="Quick Time Selection"
placeholder="Select time"
[presetTimes]="presetTimes"
helperText="Includes common time presets"
[(ngModel)]="featureValues['presets']">
</ui-time-picker>
</div>
</div>
</section>
<!-- Interactive Example -->
<section class="demo-section">
<h3>Interactive Example</h3>
<div class="demo-interactive">
<ui-time-picker
label="Meeting Time"
placeholder="Select meeting time"
helperText="Choose a time for your meeting"
[showSeconds]="true"
[(ngModel)]="interactiveValue"
(timeChange)="onTimeChange($event)">
</ui-time-picker>
<div class="demo-output">
<h4>Selected Time:</h4>
<p>{{ interactiveValue ? formatTime(interactiveValue) : 'No time selected' }}</p>
<p><strong>24-hour format:</strong> {{ interactiveValue ? format24Hour(interactiveValue) : 'N/A' }}</p>
<p><strong>Change count:</strong> {{ changeCount }}</p>
</div>
</div>
</section>
<!-- Form Integration -->
<section class="demo-section">
<h3>Form Integration</h3>
<form (ngSubmit)="onSubmit()" #demoForm="ngForm" class="demo-form">
<div class="form-row">
<ui-time-picker
label="Start Time"
name="startTime"
placeholder="Select start time"
[(ngModel)]="formValues.startTime"
[required]="true"
#startTime="ngModel">
</ui-time-picker>
<ui-time-picker
label="End Time"
name="endTime"
placeholder="Select end time"
[(ngModel)]="formValues.endTime"
[required]="true"
#endTime="ngModel">
</ui-time-picker>
</div>
<div class="form-row">
<ui-time-picker
label="Break Time"
name="breakTime"
placeholder="Select break time"
[presetTimes]="breakPresets"
[(ngModel)]="formValues.breakTime"
#breakTime="ngModel">
</ui-time-picker>
<ui-time-picker
label="Meeting Duration"
name="duration"
placeholder="Select duration"
[showSeconds]="true"
[minuteStep]="15"
[(ngModel)]="formValues.duration"
#duration="ngModel">
</ui-time-picker>
</div>
<div class="form-actions">
<button type="submit" [disabled]="!demoForm.valid" class="submit-btn">
Submit Form
</button>
<button type="button" (click)="resetForm(demoForm)" class="reset-btn">
Reset
</button>
</div>
<div class="form-status">
<p><strong>Form Valid:</strong> {{ demoForm.valid }}</p>
<p><strong>Form Values:</strong></p>
<pre>{{ formValues | json }}</pre>
</div>
</form>
</section>
</div>
`,
styleUrl: './time-picker-demo.component.scss'
})
export class TimePickerDemoComponent {
sizes = ['sm', 'md', 'lg'] as const;
variants = ['outlined', 'filled', 'underlined'] as const;
sampleValues: Record<string, TimeValue | null> = {
sm: null,
md: null,
lg: null
};
variantValues: Record<string, TimeValue | null> = {
outlined: null,
filled: null,
underlined: null
};
formatValues: Record<string, TimeValue | null> = {
twelve: null,
twentyFour: null,
withSeconds: null,
customSteps: null
};
stateValues: Record<string, TimeValue | null> = {
default: null,
error: null,
success: { hours: 9, minutes: 30, seconds: 0 },
warning: null
};
featureValues: Record<string, TimeValue | null> = {
required: null,
disabled: { hours: 12, minutes: 0, seconds: 0 },
notClearable: null,
presets: null
};
interactiveValue: TimeValue | null = null;
changeCount = 0;
formValues = {
startTime: null as TimeValue | null,
endTime: null as TimeValue | null,
breakTime: null as TimeValue | null,
duration: null as TimeValue | null
};
// Preset times for demo
presetTimes = [
{ label: '9:00 AM', value: { hours: 9, minutes: 0, seconds: 0 } },
{ label: '12:00 PM', value: { hours: 12, minutes: 0, seconds: 0 } },
{ label: '1:00 PM', value: { hours: 13, minutes: 0, seconds: 0 } },
{ label: '5:00 PM', value: { hours: 17, minutes: 0, seconds: 0 } }
];
breakPresets = [
{ label: '15 min', value: { hours: 0, minutes: 15, seconds: 0 } },
{ label: '30 min', value: { hours: 0, minutes: 30, seconds: 0 } },
{ label: '1 hour', value: { hours: 1, minutes: 0, seconds: 0 } }
];
onTimeChange(time: TimeValue | null): void {
this.changeCount++;
console.log('Time changed:', time);
}
onSubmit(): void {
console.log('Form submitted:', this.formValues);
alert('Form submitted! Check console for values.');
}
resetForm(form: any): void {
form.resetForm();
this.formValues = {
startTime: null,
endTime: null,
breakTime: null,
duration: null
};
}
formatTime(time: TimeValue): string {
const hours = time.hours;
const displayHour = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
const period = hours >= 12 ? 'PM' : 'AM';
const minutes = time.minutes.toString().padStart(2, '0');
if (time.seconds !== undefined && time.seconds > 0) {
const seconds = time.seconds.toString().padStart(2, '0');
return `${displayHour}:${minutes}:${seconds} ${period}`;
}
return `${displayHour}:${minutes} ${period}`;
}
format24Hour(time: TimeValue): string {
const hours = time.hours.toString().padStart(2, '0');
const minutes = time.minutes.toString().padStart(2, '0');
if (time.seconds !== undefined && time.seconds > 0) {
const seconds = time.seconds.toString().padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
}
return `${hours}:${minutes}`;
}
}