# Halo Widgets - Angular Guide

A complete guide for using Halo Widgets in Angular applications.

---

## 📦 Installation

### Using npm
```bash
npm install halo-widgets
```

### Using yarn
```bash
yarn add halo-widgets
```

### Using pnpm
```bash
pnpm add halo-widgets
```

---

## 🚀 Quick Start

### 1. Import CSS Styles

In your global styles file (`src/styles.css`), import the widget styles:

```css
@import "halo-widgets/css";
```

**Complete example (`src/styles.css`):**
```css
@import "halo-widgets/css";
/* Your other global styles */
```

### 2. Create Widget Components

Create Angular wrapper components for the widgets you need. Here's an example for TextInput:

```typescript
// src/app/components/text-input.component.ts
import {
  Component,
  Input,
  Output,
  EventEmitter,
  ElementRef,
  AfterViewInit,
  OnDestroy,
} from '@angular/core';
import { getWidget } from 'halo-widgets';

@Component({
  selector: 'halo-text-input',
  standalone: true,
  template: '<div></div>',
})
export class TextInputComponent implements AfterViewInit, OnDestroy {
  @Input() label?: string;
  @Input() placeholder?: string;
  @Input() helperText?: string;
  @Input() value?: string;
  @Input() required?: boolean;
  @Input() minLength?: number;
  @Input() maxLength?: number;
  @Input() counter?: boolean;
  @Input() clearable?: boolean;

  @Output() change = new EventEmitter<string>();
  @Output() validate = new EventEmitter<{isValid: boolean, error?: string}>();

  private instance: any;

  constructor(private elementRef: ElementRef) {}

  ngAfterViewInit() {
    const widget = getWidget('text-input');
    this.instance = widget.init(this.elementRef.nativeElement, {
      label: this.label,
      placeholder: this.placeholder,
      helperText: this.helperText,
      value: this.value,
      required: this.required,
      minLength: this.minLength,
      maxLength: this.maxLength,
      counter: this.counter,
      clearable: this.clearable,
      onChange: (value: string) => this.change.emit(value),
      onValidate: (isValid: boolean, error?: string) => 
        this.validate.emit({isValid, error})
    });
  }

  ngOnDestroy() {
    this.instance?.destroy?.();
  }
}
```

### 3. Use in Components

```typescript
// src/app/app.component.ts
import { Component } from '@angular/core';
import { TextInputComponent } from './components/text-input.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TextInputComponent],
  template: `
    <halo-text-input
      label="Username"
      placeholder="Enter your username"
      [value]="username"
      [required]="true"
      (change)="handleUsernameChange($event)"
    />
  `,
})
export class AppComponent {
  username = '';

  handleUsernameChange(value: string) {
    this.username = value;
  }
}
```

---

## 📚 Available Widgets

All widgets are created using the core `getWidget()` function from `halo-widgets`:

```typescript
import { getWidget } from 'halo-widgets';

// Available widget IDs:
const widgets = [
  'text-input',        // Text input with validation & suggestions
  'password-input',    // Password with strength meter & generator
  'email-input',       // Email with domain suggestions
  'phone-input',       // International phone with country picker
  'date-input',        // Date picker (single, range, month)
  'location-input',    // Google Places autocomplete
  'number-input',      // Numeric input with formatting
  'slider-input',      // Range slider
  'radio-input',       // Radio button group
  'checkbox-input',    // Checkbox group (single or multi-select)
  'dropdown-input',    // Searchable dropdown
  'textarea-input',    // Multi-line text input
];
```

---

## 💡 Complete Widget Examples

### TextInput - Basic Usage

```typescript
// text-input.component.ts
@Component({
  selector: 'halo-text-input',
  standalone: true,
  template: '<div></div>',
})
export class TextInputComponent implements AfterViewInit, OnDestroy {
  @Input() label?: string;
  @Input() placeholder?: string;
  @Input() helperText?: string;
  @Input() value?: string;
  @Input() required?: boolean;
  @Input() minLength?: number;
  @Input() maxLength?: number;
  @Input() counter?: boolean;
  @Input() clearable?: boolean;
  @Input() allowSpaces?: boolean;
  @Input() caseTransform?: string;

  @Output() change = new EventEmitter<string>();
  @Output() validate = new EventEmitter<{isValid: boolean, error?: string}>();

  private instance: any;

  constructor(private elementRef: ElementRef) {}

  ngAfterViewInit() {
    const widget = getWidget('text-input');
    this.instance = widget.init(this.elementRef.nativeElement, {
      label: this.label,
      placeholder: this.placeholder,
      helperText: this.helperText,
      value: this.value,
      required: this.required,
      minLength: this.minLength,
      maxLength: this.maxLength,
      counter: this.counter,
      clearable: this.clearable,
      allowSpaces: this.allowSpaces,
      caseTransform: this.caseTransform,
      onChange: (value: string) => this.change.emit(value),
      onValidate: (isValid: boolean, error?: string) => 
        this.validate.emit({isValid, error})
    });
  }

  ngOnDestroy() {
    this.instance?.destroy?.();
  }
}
```

**Usage:**
```html
<halo-text-input
  label="Username"
  placeholder="Enter your username"
  helperText="3-20 characters, lowercase only"
  [value]="username"
  [required]="true"
  [minLength]="3"
  [maxLength]="20"
  [counter]="true"
  [clearable]="true"
  [allowSpaces]="false"
  caseTransform="lowercase"
  (change)="handleUsernameChange($event)"
  (validate)="handleValidation($event)"
/>
```

### PasswordInput - With Strength Meter

```typescript
// password-input.component.ts
@Component({
  selector: 'halo-password-input',
  standalone: true,
  template: '<div></div>',
})
export class PasswordInputComponent implements AfterViewInit, OnDestroy {
  @Input() label?: string;
  @Input() placeholder?: string;
  @Input() helperText?: string;
  @Input() value?: string;
  @Input() required?: boolean;
  @Input() minLength?: number;
  @Input() showToggle?: boolean;
  @Input() showStrength?: boolean;
  @Input() showRequirements?: boolean;
  @Input() showGenerator?: boolean;
  @Input() requireLowercase?: boolean;
  @Input() requireUppercase?: boolean;
  @Input() requireNumber?: boolean;
  @Input() requireSymbol?: boolean;

  @Output() change = new EventEmitter<string>();
  @Output() strengthChange = new EventEmitter<{score: number, feedback: string}>();

  private instance: any;

  constructor(private elementRef: ElementRef) {}

  ngAfterViewInit() {
    const widget = getWidget('password-input');
    this.instance = widget.init(this.elementRef.nativeElement, {
      label: this.label,
      placeholder: this.placeholder,
      helperText: this.helperText,
      value: this.value,
      required: this.required,
      minLength: this.minLength,
      showToggle: this.showToggle,
      showStrength: this.showStrength,
      showRequirements: this.showRequirements,
      showGenerator: this.showGenerator,
      requireLowercase: this.requireLowercase,
      requireUppercase: this.requireUppercase,
      requireNumber: this.requireNumber,
      requireSymbol: this.requireSymbol,
      onChange: (value: string) => this.change.emit(value),
      onStrengthChange: (score: number, feedback: string) => 
        this.strengthChange.emit({score, feedback})
    });
  }

  ngOnDestroy() {
    this.instance?.destroy?.();
  }
}
```

**Usage:**
```html
<halo-password-input
  label="Password"
  placeholder="Enter a strong password"
  helperText="At least 8 characters with uppercase, lowercase, number, and symbol"
  [value]="password"
  [required]="true"
  [minLength]="8"
  [showToggle]="true"
  [showStrength]="true"
  [showRequirements]="true"
  [showGenerator]="true"
  [requireLowercase]="true"
  [requireUppercase]="true"
  [requireNumber]="true"
  [requireSymbol]="true"
  (change)="handlePasswordChange($event)"
  (strengthChange)="handleStrengthChange($event)"
/>
```

### DropdownInput - Searchable

```typescript
// dropdown-input.component.ts
@Component({
  selector: 'halo-dropdown-input',
  standalone: true,
  template: '<div></div>',
})
export class DropdownInputComponent implements AfterViewInit, OnDestroy {
  @Input() label?: string;
  @Input() placeholder?: string;
  @Input() helperText?: string;
  @Input() options: Array<{label: string, value: any}> = [];
  @Input() multiple?: boolean;
  @Input() value?: any;
  @Input() searchable?: boolean;
  @Input() required?: boolean;

  @Output() change = new EventEmitter<any>();

  private instance: any;

  constructor(private elementRef: ElementRef) {}

  ngAfterViewInit() {
    const widget = getWidget('dropdown-input');
    this.instance = widget.init(this.elementRef.nativeElement, {
      label: this.label,
      placeholder: this.placeholder,
      helperText: this.helperText,
      options: this.options,
      multiple: this.multiple,
      value: this.value,
      searchable: this.searchable,
      required: this.required,
      onChange: (value: any) => this.change.emit(value)
    });
  }

  ngOnDestroy() {
    this.instance?.destroy?.();
  }
}
```

**Usage:**
```html
<!-- Single Select -->
<halo-dropdown-input
  label="Select Country"
  placeholder="Choose a country..."
  [options]="countryOptions"
  [value]="country"
  [searchable]="true"
  [required]="true"
  (change)="handleCountryChange($event)"
/>

<!-- Multi-Select -->
<halo-dropdown-input
  label="Select Countries"
  placeholder="Choose multiple countries..."
  [options]="countryOptions"
  [value]="countries"
  [multiple]="true"
  [searchable]="true"
  (change)="handleCountriesChange($event)"
/>
```

### LocationInput - Google Places

```typescript
// location-input.component.ts
@Component({
  selector: 'halo-location-input',
  standalone: true,
  template: `
    <div class="halo-field">
      <label class="halo-label">{{ label }}</label>
      <div class="halo-text-root">
        <input
          type="text"
          class="halo-text-input"
          [placeholder]="placeholder"
          disabled
        />
      </div>
      <div class="halo-note" style="color: #f59e0b;">
        ⚠️ Location widget requires Google Places API key to function
      </div>
    </div>
  `,
})
export class LocationInputComponent {
  @Input() label?: string;
  @Input() placeholder?: string;
  @Input() helperText?: string;
  @Input() value?: string;
  @Input() allowCoordinates?: boolean;
  @Input() apiKey?: string;

  @Output() change = new EventEmitter<any>();
}
```

**Usage:**
```html
<halo-location-input
  label="Location"
  placeholder="Search for a location"
  helperText="Start typing to search"
  [value]="location"
  apiKey="YOUR_GOOGLE_MAPS_API_KEY"
  [allowCoordinates]="true"
  (change)="handleLocationChange($event)"
/>
```

---

## 🎯 Complete Form Example

```typescript
// app.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { 
  TextInputComponent,
  EmailInputComponent,
  PasswordInputComponent,
  DropdownInputComponent,
  CheckboxInputComponent
} from './components';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    CommonModule,
    TextInputComponent,
    EmailInputComponent,
    PasswordInputComponent,
    DropdownInputComponent,
    CheckboxInputComponent
  ],
  template: `
    <form (ngSubmit)="handleSubmit()" class="form">
      <h1>Registration Form</h1>

      <halo-text-input
        label="Username"
        placeholder="Enter username"
        [value]="formData.username"
        [required]="true"
        [minLength]="3"
        [maxLength]="20"
        [counter]="true"
        (change)="handleUsernameChange($event)"
      />

      <halo-email-input
        label="Email"
        placeholder="you@example.com"
        [value]="formData.email"
        [required]="true"
        [showDomainSuggestions]="true"
        (change)="handleEmailChange($event)"
      />

      <halo-password-input
        label="Password"
        placeholder="Enter password"
        [value]="formData.password"
        [required]="true"
        [minLength]="8"
        [showStrength]="true"
        [showToggle]="true"
        (change)="handlePasswordChange($event)"
      />

      <halo-dropdown-input
        label="Country"
        placeholder="Select your country"
        [options]="countryOptions"
        [value]="formData.country"
        [searchable]="true"
        [required]="true"
        (change)="handleCountryChange($event)"
      />

      <halo-checkbox-input
        label="Interests"
        helperText="Select your areas of interest"
        [options]="interestOptions"
        [value]="formData.interests"
        (change)="handleInterestsChange($event)"
      />

      <button type="submit" [disabled]="!isFormValid()">
        Register
      </button>
    </form>
  `,
  styles: [`
    .form {
      max-width: 500px;
      margin: 2rem auto;
      padding: 2rem;
      border: 1px solid #e5e7eb;
      border-radius: 8px;
    }
    
    h1 {
      margin-bottom: 2rem;
      text-align: center;
    }
    
    button {
      width: 100%;
      padding: 0.75rem;
      margin-top: 1rem;
      background: #6366f1;
      color: white;
      border: none;
      border-radius: 6px;
      cursor: pointer;
    }
    
    button:disabled {
      background: #9ca3af;
      cursor: not-allowed;
    }
  `]
})
export class AppComponent {
  formData = {
    username: '',
    email: '',
    password: '',
    country: '',
    interests: [] as string[]
  };

  countryOptions = [
    { label: 'United States', value: 'us' },
    { label: 'Canada', value: 'ca' },
    { label: 'United Kingdom', value: 'uk' },
    { label: 'Germany', value: 'de' },
    { label: 'France', value: 'fr' }
  ];

  interestOptions = [
    { label: 'Web Development', value: 'web' },
    { label: 'Mobile Apps', value: 'mobile' },
    { label: 'AI & Machine Learning', value: 'ai' },
    { label: 'Data Science', value: 'data' }
  ];

  handleUsernameChange(value: string) {
    this.formData.username = value;
  }

  handleEmailChange(value: string) {
    this.formData.email = value;
  }

  handlePasswordChange(value: string) {
    this.formData.password = value;
  }

  handleCountryChange(value: string) {
    this.formData.country = value;
  }

  handleInterestsChange(value: string[]) {
    this.formData.interests = value;
  }

  isFormValid(): boolean {
    return !!(
      this.formData.username &&
      this.formData.email &&
      this.formData.password &&
      this.formData.country
    );
  }

  handleSubmit() {
    if (this.isFormValid()) {
      console.log('Form submitted:', this.formData);
    }
  }
}
```

---

## 🎨 Styling & Theming

### Using CSS Variables

Customize widget appearance with CSS variables in your global styles:

```css
/* src/styles.css */
@import "halo-widgets/css";

:root {
  --halo-color-primary: #6366f1;
  --halo-color-accent: #8b5cf6;
  --halo-color-surface: #ffffff;
  --halo-color-bg: #f9fafb;
  --halo-color-text: #111827;
  --halo-color-muted: #6b7280;
  --halo-color-border: #e5e7eb;
  --halo-color-error: #ef4444;
  --halo-color-success: #10b981;
  --halo-color-warning: #f59e0b;
  --halo-font-family: system-ui, -apple-system, sans-serif;
  --halo-radius: 8px;
  --halo-border: 1px solid var(--halo-color-border);
}
```

### Component-Specific Styling

Add custom classes to your components:

```typescript
@Component({
  template: `
    <div class="custom-wrapper">
      <halo-text-input
        class="custom-input"
        label="Username"
        [value]="username"
        (change)="handleChange($event)"
      />
    </div>
  `,
  styles: [`
    .custom-wrapper {
      max-width: 400px;
      margin: 1rem 0;
    }
    
    .custom-input {
      border-radius: 12px;
    }
  `]
})
```

---

## 🔧 Advanced Patterns

### Custom Validation

```typescript
// In your component
handleUsernameValidation(event: {isValid: boolean, error?: string}) {
  if (!event.isValid) {
    console.log('Validation error:', event.error);
    // Show error message to user
  }
}

// Custom validation function
validateUsername(value: string): string | null {
  if (value.includes('admin')) {
    return "Username cannot contain 'admin'";
  }
  if (value.length < 3) {
    return 'Username must be at least 3 characters';
  }
  return null; // null means valid
}
```

### Reactive Forms Integration

```typescript
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';

@Component({
  selector: 'app-reactive-form',
  standalone: true,
  imports: [ReactiveFormsModule, TextInputComponent],
  template: `
    <form [formGroup]="userForm" (ngSubmit)="onSubmit()">
      <halo-text-input
        label="Username"
        [value]="userForm.get('username')?.value || ''"
        [required]="true"
        (change)="updateFormControl('username', $event)"
      />
      
      <button type="submit" [disabled]="userForm.invalid">
        Submit
      </button>
    </form>
  `
})
export class ReactiveFormComponent {
  userForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.userForm = this.fb.group({
      username: ['', [Validators.required, Validators.minLength(3)]]
    });
  }

  updateFormControl(controlName: string, value: any) {
    this.userForm.get(controlName)?.setValue(value);
  }

  onSubmit() {
    if (this.userForm.valid) {
      console.log(this.userForm.value);
    }
  }
}
```

### Debounced API Calls

```typescript
@Component({
  template: `
    <halo-text-input
      label="Search"
      placeholder="Type to search..."
      [value]="query"
      [debounceMs]="500"
      (change)="handleSearch($event)"
    />
  `
})
export class SearchComponent {
  query = '';

  handleSearch(value: string) {
    this.query = value;
    // This will be debounced by 500ms
    this.performSearch(value);
  }

  private performSearch(query: string) {
    // Make API call
    console.log('Searching for:', query);
  }
}
```

---

## 🐛 Troubleshooting

### Styles Not Applied

**Problem:** Widgets appear unstyled

**Solution:** Make sure you imported the CSS in `src/styles.css`:
```css
@import "halo-widgets/css";
```

### Widget Not Initializing

**Problem:** Widget doesn't render or throws errors

**Solution:** Ensure you're using `AfterViewInit` and `OnDestroy`:
```typescript
export class MyWidgetComponent implements AfterViewInit, OnDestroy {
  ngAfterViewInit() {
    // Initialize widget here
  }

  ngOnDestroy() {
    // Cleanup widget here
    this.instance?.destroy?.();
  }
}
```

### TypeScript Errors

**Problem:** Type errors on widget initialization

**Solution:** Use `any` type for the widget instance:
```typescript
private instance: any; // Use any type for widget instances
```

### Memory Leaks

**Problem:** Components not cleaning up properly

**Solution:** Always implement `OnDestroy` and call `destroy()`:
```typescript
ngOnDestroy() {
  this.instance?.destroy?.();
}
```

---

## 📋 Requirements

- Angular 15.0.0 or higher
- TypeScript 4.8+ (recommended)
- Node.js 16+ (for development)

---

## 🔗 Additional Resources

- **Props Reference:** See `PROPS_REFERENCE.md` for complete prop documentation
- **Examples:** Check the test component for working examples
- **Package Version:** v1.1.5

---

## 💪 Best Practices

1. **Always import CSS** in your global styles file
2. **Use property binding** `[prop]="value"` for dynamic values
3. **Use event binding** `(change)="handler($event)"` for events
4. **Implement lifecycle hooks** properly (`AfterViewInit`, `OnDestroy`)
5. **Handle null values** appropriately for optional props
6. **Use TypeScript interfaces** for better type safety
7. **Debounce API calls** to reduce server load
8. **Provide helper text** to guide users
9. **Mark required fields** with `[required]="true"`
10. **Test validation** with event handlers

---

## 🎉 Quick Start Template

Copy this template to get started quickly:

```typescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TextInputComponent, EmailInputComponent, PasswordInputComponent } from './components';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, TextInputComponent, EmailInputComponent, PasswordInputComponent],
  template: `
    <form (ngSubmit)="handleSubmit()" class="form">
      <halo-text-input
        label="Username"
        placeholder="Enter username"
        [value]="username"
        [required]="true"
        [minLength]="3"
        [maxLength]="20"
        [counter]="true"
        (change)="handleUsernameChange($event)"
      />

      <halo-email-input
        label="Email"
        placeholder="you@example.com"
        [value]="email"
        [required]="true"
        [showDomainSuggestions]="true"
        (change)="handleEmailChange($event)"
      />

      <halo-password-input
        label="Password"
        placeholder="Enter password"
        [value]="password"
        [required]="true"
        [minLength]="8"
        [showStrength]="true"
        [showToggle]="true"
        (change)="handlePasswordChange($event)"
      />

      <button type="submit">Submit</button>
    </form>
  `,
  styles: [`
    .form {
      max-width: 500px;
      margin: 2rem auto;
      padding: 2rem;
    }
  `]
})
export class AppComponent {
  username = '';
  email = '';
  password = '';

  handleUsernameChange(value: string) {
    this.username = value;
  }

  handleEmailChange(value: string) {
    this.email = value;
  }

  handlePasswordChange(value: string) {
    this.password = value;
  }

  handleSubmit() {
    console.log({ 
      username: this.username, 
      email: this.email, 
      password: this.password 
    });
  }
}
```

---

**Happy coding with Halo Widgets in Angular! 🚀**
