# Halo Widgets

A feature-rich, framework-agnostic widgets library built with vanilla HTML, CSS, and TypeScript. Fully compatible with React, Vue, Angular, and Svelte.

## Installation

```bash
npm install @halo-widgets/core
```

## Framework Support

### React
```tsx
import { TextInput, PasswordInput, SliderInput } from "@halo-widgets/react";

<TextInput placeholder="Enter text" maxLength={100} />
```

### Vue
```vue
<script setup>
import { TextInput } from "@halo-widgets/vue";
</script>

<template>
  <TextInput placeholder="Enter text" :maxLength="100" />
</template>
```

### Angular
```typescript
import { TextInputComponent } from "@halo-widgets/angular";

@Component({
  imports: [TextInputComponent],
  template: '<halo-text-input [config]="{ placeholder: 'Enter text' }" />'
})
```

### Svelte
```svelte
<script>
  import { TextInput } from "@halo-widgets/svelte";
</script>

<TextInput placeholder="Enter text" maxLength={100} />
```

---

## Widgets

### 1. Text Input

A powerful text input with validation, suggestions, character counter, and more.

```tsx
import { TextInput } from "@halo-widgets/react";

<TextInput
  // Basic props
  id="username"
  name="username"
  label="Username"
  placeholder="Enter your username"
  helperText="Choose a unique username"
  
  // Value
  value="john_doe"
  defaultValue=""
  
  // Validation
  required={true}
  minLength={3}
  maxLength={20}
  pattern="^[a-zA-Z0-9_]+$"
  
  // Character handling
  allowSpaces={false}  // Set to true for full names/addresses
  lowercase={true}     // Auto convert to lowercase
  uppercase={false}    // Auto convert to uppercase
  trim={true}          // Remove leading/trailing spaces
  
  // UI Features
  showCounter={true}         // Show character count
  clearable={true}           // Show clear button
  showSuggestions={true}     // Enable suggestions dropdown
  suggestionsApi="https://api.datamuse.com/sug?s="  // API for suggestions
  debounceMs={300}           // Debounce delay for API calls
  
  // State
  disabled={false}
  readOnly={false}
  
  // Callbacks
  onChange={(value) => console.log(value)}
  onValidate={(isValid, error) => console.log(isValid, error)}
  
  // Styling
  className="custom-class"
/>
```

**Props:**
- `id`, `name`, `label`, `placeholder`, `helperText` - Standard form field props
- `value`, `defaultValue` - Controlled/uncontrolled value
- `required`, `minLength`, `maxLength`, `pattern` - Validation rules
- `allowSpaces` - Allow spaces in input (default: true)
- `lowercase`, `uppercase`, `trim` - Text transformation
- `showCounter` - Display character counter
- `clearable` - Show clear button
- `showSuggestions` - Enable suggestions dropdown
- `suggestionsApi` - API endpoint for suggestions (query appended to URL)
- `debounceMs` - Debounce delay for onChange and API calls (default: 300)
- `disabled`, `readOnly` - Input state
- `onChange(value)` - Fired on value change
- `onValidate(isValid, error?)` - Fired on validation

---

### 2. Password Input

Password input with strength meter, requirements checker, generator, visibility toggle, and breach detection.

```tsx
import { PasswordInput } from "@halo-widgets/react";

<PasswordInput
  // Basic props
  label="Password"
  placeholder="Enter a strong password"
  helperText="Must be at least 10 characters"
  
  // Visibility toggle
  showVisibilityToggle={true}  // Eye icon to show/hide password
  
  // Strength meter
  showStrength={true}          // Show password strength bar
  strengthConfig={{
    minLength: 10,
    requireLowercase: true,
    requireUppercase: true,
    requireNumber: true,
    requireSymbol: true,
  }}
  
  // Requirements checklist
  showRequirements={true}      // Show requirements list
  
  // Password generator
  showGenerator={true}         // Show "Generate" button
  generatorConfig={{
    length: 16,
    lowercase: true,
    uppercase: true,
    numbers: true,
    symbols: true,
  }}
  
  // Copy to clipboard
  showCopyButton={true}        // Show copy button
  
  // Breach detection (HaveIBeenPwned API)
  checkPwned={true}            // Check if password is compromised
  pwnedDebounceMs={1000}       // Delay before checking
  
  // Validation
  required={true}
  minLength={10}
  maxLength={128}
  
  // Callbacks
  onChange={(value) => console.log(value)}
  onStrengthChange={(score, feedback) => console.log(score, feedback)}
  onValidate={(isValid, error) => console.log(isValid, error)}
/>
```

**Props:**
- `showVisibilityToggle` - Show eye icon to toggle visibility
- `showStrength` - Display strength meter (Weak/Fair/Good/Strong)
- `strengthConfig` - Customize strength requirements
- `showRequirements` - Display requirements checklist
- `showGenerator` - Show password generator button
- `generatorConfig` - Customize generated password format
- `showCopyButton` - Show copy to clipboard button
- `checkPwned` - Check against HaveIBeenPwned API
- `pwnedDebounceMs` - Debounce delay for breach check
- `onStrengthChange(score, feedback)` - Fired when strength changes

---

### 3. Email Input

Email input with validation, multiple emails (chips), domain suggestions, and Gravatar integration.

```tsx
import { EmailInput } from "@halo-widgets/react";

<EmailInput
  // Basic props
  label="Email Address"
  placeholder="Enter your email"
  
  // Multiple emails
  multiple={false}             // Allow multiple emails as chips
  maxEmails={5}                // Max number of emails (if multiple)
  
  // Validation
  required={true}
  validateFormat={true}        // Basic regex validation
  validateDomain={true}        // Check for valid TLD
  allowedDomains={["gmail.com", "company.com"]}  // Whitelist domains
  blockedDomains={["tempmail.com"]}              // Blacklist domains
  
  // Plus addressing
  allowPlusAddressing={true}   // Allow email+tag@domain.com
  normalizePlusAddressing={false}  // Strip +tag when normalizing
  
  // Domain suggestions
  showDomainSuggestions={true} // Suggest common domains
  commonDomains={["gmail.com", "yahoo.com", "outlook.com"]}
  
  // Gravatar
  showGravatar={true}          // Show user avatar from Gravatar
  gravatarSize={40}            // Avatar size in pixels
  
  // UI
  lowercase={true}             // Auto convert to lowercase
  trim={true}                  // Auto trim spaces
  
  // Callbacks
  onChange={(value) => console.log(value)}  // string or string[]
  onValidate={(isValid, error) => console.log(isValid, error)}
/>
```

**Props:**
- `multiple` - Enable multiple email input with chips
- `maxEmails` - Maximum number of emails (for multiple mode)
- `validateFormat` - Email regex validation
- `validateDomain` - Check for valid domain and TLD
- `allowedDomains` - Only allow specific domains
- `blockedDomains` - Block specific domains
- `allowPlusAddressing` - Allow plus addressing (email+tag@domain.com)
- `normalizePlusAddressing` - Strip +tag portion
- `showDomainSuggestions` - Show common domain suggestions
- `commonDomains` - List of domains to suggest
- `showGravatar` - Display user avatar from Gravatar
- `gravatarSize` - Avatar image size

---

### 4. Phone Input

International phone input with country selection, formatting, and validation.

```tsx
import { PhoneInput } from "@halo-widgets/react";

<PhoneInput
  // Basic props
  label="Phone Number"
  placeholder="Enter phone number"
  
  // Country selection
  allowCountrySelect={true}    // Show country dropdown
  defaultCountry="US"          // Default country code (ISO 3166-1 alpha-2)
  preferredCountries={["US", "GB", "CA"]}  // Show these first
  onlyCountries={["US", "CA", "MX"]}       // Restrict to these
  
  // Dial code
  separateDialCode={true}      // Show dial code separately (+1)
  
  // Formatting
  format="national"            // "national" | "international" | "e164"
  autoFormat={true}            // Auto-format as user types
  
  // Validation
  required={true}
  validateOnChange={true}      // Validate while typing
  strictValidation={false}     // Strict digit count validation
  minDigits={10}
  maxDigits={15}
  
  // Callbacks
  onChange={(value, country, formatted) => console.log(value, country, formatted)}
  onCountryChange={(country) => console.log(country)}
  onValidate={(isValid, error) => console.log(isValid, error)}
/>
```

**Props:**
- `allowCountrySelect` - Show country picker dropdown
- `defaultCountry` - Initial country (ISO 3166-1 alpha-2 code)
- `preferredCountries` - Countries shown at top of dropdown
- `onlyCountries` - Restrict to specific countries
- `separateDialCode` - Display dial code separately (e.g., +1)
- `format` - Output format: "national", "international", or "e164"
- `autoFormat` - Auto-format number as user types
- `validateOnChange` - Validate in real-time
- `strictValidation` - Enforce exact digit count per country
- `minDigits`, `maxDigits` - Digit count validation
- `onCountryChange(country)` - Fired when country changes

---

### 5. Location Input

Location input with Google Places Autocomplete, coordinate parsing, and custom dropdown.

```tsx
import { LocationInput } from "@halo-widgets/react";

<LocationInput
  // Google Maps API
  apiKey="YOUR_GOOGLE_MAPS_API_KEY"  // Required
  
  // Basic props
  label="Location"
  placeholder="Search for a location"
  
  // Search configuration
  countryCodes={[]}            // Restrict to countries (ISO 3166-1 alpha-2)
  types={["geocode"]}          // Place types: geocode, address, establishment, etc.
  
  // Features
  allowCoordinates={true}      // Allow direct coordinate input (lat,lng)
  showCurrentLocation={false}  // Show "Use current location" button
  
  // UI
  debounceMs={300}             // Debounce search requests
  
  // Callbacks
  onChange={(value) => console.log(value)}
  onPlaceSelect={(place) => console.log(place)}  // Full place details
  onValidate={(isValid, error) => console.log(isValid, error)}
/>
```

**Props:**
- `apiKey` - Google Maps API key (required)
- `countryCodes` - Restrict search to specific countries (e.g., ["US", "GB"])
- `types` - Place types to search (geocode, address, establishment, etc.)
- `allowCoordinates` - Allow direct coordinate input (e.g., "40.7128, -74.0060")
- `showCurrentLocation` - Show button to get user's current location
- `debounceMs` - Delay before sending search requests
- `onPlaceSelect(place)` - Returns full place details (address components, geometry, etc.)

---

### 6. Date Input

Date picker with single date, range, and month-only modes.

```tsx
import { DateInput } from "@halo-widgets/react";

<DateInput
  // Basic props
  label="Select Date"
  placeholder="Pick a date"
  
  // Mode
  mode="single"                // "single" | "range" | "month"
  
  // Date constraints
  min="2020-01-01"             // Minimum selectable date
  max="2030-12-31"             // Maximum selectable date
  
  // Value (format: YYYY-MM-DD)
  value="2024-03-15"           // For single mode
  value={["2024-03-01", "2024-03-31"]}  // For range mode
  
  // Formatting
  format="MM/DD/YYYY"          // Display format for single/range
  monthFormat="MMM YYYY"       // Display format for month mode (e.g., "Mar 2024")
  
  // Calendar configuration
  weekStartsOn={0}             // 0=Sunday, 1=Monday
  showShortcuts={true}         // Show Today/Clear buttons
  
  // Callbacks
  onChange={(value) => console.log(value)}  // string or [string, string]
  onValidate={(isValid, error) => console.log(isValid, error)}
/>
```

**Props:**
- `mode` - Selection mode: "single", "range", or "month"
- `min`, `max` - Date range constraints (YYYY-MM-DD format)
- `value` - Current value (string for single/month, tuple for range)
- `format` - Display format (e.g., "MM/DD/YYYY", "DD-MM-YYYY")
- `monthFormat` - Format for month-only mode (e.g., "MMM YYYY")
- `weekStartsOn` - First day of week (0=Sunday, 1=Monday)
- `showShortcuts` - Show Today/Clear action buttons

---

### 7. Number Input

Number input with formatting, validation, steppers, and special formats (currency, percentage).

```tsx
import { NumberInput } from "@halo-widgets/react";

<NumberInput
  // Basic props
  label="Amount"
  placeholder="0"
  
  // Value
  value={42}
  defaultValue={0}
  
  // Range
  min={0}
  max={1000}
  step={1}                     // Increment/decrement step
  
  // Precision
  precision={2}                // Decimal places (e.g., 2 for currency)
  
  // Formatting
  thousandSeparator={true}     // Show thousands separator (1,000)
  locale="en-US"               // Number formatting locale
  prefix="$"                   // Prefix (e.g., "$" for currency)
  suffix="%"                   // Suffix (e.g., "%" for percentage)
  
  // Validation
  required={true}
  allowNegative={true}         // Allow negative numbers
  
  // UI Features
  showSteppers={false}         // Show +/- buttons
  
  // Callbacks
  onChange={(value) => console.log(value)}
  onValidate={(isValid, error) => console.log(isValid, error)}
/>
```

**Props:**
- `value`, `defaultValue` - Number value
- `min`, `max` - Range constraints
- `step` - Increment/decrement amount
- `precision` - Decimal places (0 for integers)
- `thousandSeparator` - Show comma separators (e.g., 1,000)
- `locale` - Locale for number formatting
- `prefix`, `suffix` - Add symbols (e.g., "$", "%", "kg")
- `allowNegative` - Allow negative numbers
- `showSteppers` - Display increment/decrement buttons

**Common use cases:**
```tsx
// Currency
<NumberInput prefix="$" precision={2} min={0} />

// Percentage
<NumberInput suffix="%" min={0} max={100} />

// Age
<NumberInput min={0} max={120} precision={0} />
```

---

### 8. Textarea Input

Multi-line text input with auto-grow, character counter, and suggestions.

```tsx
import { TextareaInput } from "@halo-widgets/react";

<TextareaInput
  // Basic props
  label="Description"
  placeholder="Enter description"
  helperText="Describe your project"
  
  // Size
  rows={4}                     // Initial visible rows
  autoGrow={true}              // Expand as user types
  maxHeight={300}              // Max height in pixels (if autoGrow)
  
  // Length constraints
  minLength={10}
  maxLength={500}
  showCounter={true}           // Show character counter
  
  // UI Features
  clearable={true}             // Show clear button
  
  // Behavior
  debounceMs={300}             // Debounce onChange calls
  onEnter="newline"            // "newline" | "submit"
  
  // Validation
  required={true}
  
  // Callbacks
  onChange={(value) => console.log(value)}
  onValidate={(isValid, error) => console.log(isValid, error)}
/>
```

**Props:**
- `rows` - Initial number of visible rows
- `autoGrow` - Automatically expand height as content grows
- `maxHeight` - Maximum height in pixels (when autoGrow enabled)
- `minLength`, `maxLength` - Character length validation
- `showCounter` - Display character counter
- `clearable` - Show clear button
- `debounceMs` - Delay before firing onChange
- `onEnter` - Behavior when Enter is pressed: "newline" or "submit"

---

### 9. Dropdown Input

Searchable dropdown with local/API options, single/multi-select, and chips.

```tsx
import { DropdownInput } from "@halo-widgets/react";

<DropdownInput
  // Basic props
  label="Select Country"
  placeholder="Choose..."
  
  // Options (local)
  options={[
    { label: "United States", value: "us" },
    { label: "Canada", value: "ca" },
    { label: "Mexico", value: "mx" },
  ]}
  
  // Options (API)
  optionsApi="https://api.example.com/search?q="
  optionsMap={{                // Map API response to { label, value }
    label: "name",
    value: "id",
  }}
  
  // Search
  searchable={true}            // Enable search
  minCharsForSearch={1}        // Min characters before searching API
  debounceMs={300}             // Debounce API requests
  
  // Selection
  multiple={false}             // Allow multiple selection
  maxSelected={3}              // Max items (if multiple)
  
  // Value
  value="us"                   // For single select
  value={["us", "ca"]}         // For multi-select
  
  // Validation
  required={true}
  
  // Callbacks
  onChange={(value) => console.log(value)}  // string or string[]
  onValidate={(isValid, error) => console.log(isValid, error)}
/>
```

**Props:**
- `options` - Local options array: `{ label: string, value: string }[]`
- `optionsApi` - API endpoint for dynamic options (query appended to URL)
- `optionsMap` - Map API response fields to label/value
- `searchable` - Enable search/filter functionality
- `minCharsForSearch` - Minimum characters before triggering API search
- `debounceMs` - Delay before API requests
- `multiple` - Allow multiple selection with chips
- `maxSelected` - Maximum number of selections (multi-select only)
- `value` - Current value (string for single, array for multi)

**API Response Format:**
```json
{
  "results": [
    { "name": "Option 1", "id": "opt1" },
    { "name": "Option 2", "id": "opt2" }
  ]
}
```

With `optionsMap: { label: "name", value: "id" }`, the dropdown will display "Option 1" and use "opt1" as the value.

---

### 10. Checkbox Input

Checkbox group with select-all, single/multi-select, and validation.

```tsx
import { CheckboxInput } from "@halo-widgets/react";

<CheckboxInput
  // Basic props
  label="Select your interests"
  helperText="Choose at least one"
  
  // Options
  options={[
    { label: "AI", value: "ai" },
    { label: "Web Development", value: "web" },
    { label: "Mobile Apps", value: "mobile" },
    { label: "Data Science", value: "data", disabled: true },
  ]}
  
  // Mode
  single={false}               // Single-select mode (like radio)
  
  // Value
  value={["ai", "web"]}        // For multi-select
  defaultValue={["ai"]}        // Default value
  
  singleValue="web"            // For single mode
  
  // Validation
  required={true}
  minSelected={1}              // Min selections (multi-select)
  maxSelected={3}              // Max selections (multi-select)
  
  // UI Features
  selectAll={true}             // Show "Select all" option
  
  // Styling
  boxBackground="#fff"         // Checkbox background color
  checkColor="#6366f1"         // Checkmark color
  
  // State
  disabled={false}
  readOnly={false}
  
  // Callbacks
  onChange={(values) => console.log(values)}          // For multi-select
  onChangeSingle={(value) => console.log(value)}      // For single mode
  onValidate={(isValid, error) => console.log(isValid, error)}
/>
```

**Props:**
- `options` - Array of options: `{ label: string, value: string, disabled?: boolean }[]`
- `single` - Single-select mode (behaves like radio but styled as checkbox)
- `value`, `defaultValue` - Selected values (array for multi, string for single)
- `singleValue` - Current value in single mode
- `required` - At least one must be selected
- `minSelected`, `maxSelected` - Selection count validation (multi-select)
- `selectAll` - Show "Select all" checkbox at top
- `boxBackground` - Checkbox box background color
- `checkColor` - Checkmark/tick color
- `disabled`, `readOnly` - Disable interaction
- `onChangeSingle(value)` - Callback for single mode

---

### 11. Radio Input

Radio button group with validation and custom styling.

```tsx
import { RadioInput } from "@halo-widgets/react";

<RadioInput
  // Basic props
  label="Choose a plan"
  helperText="Select your subscription tier"
  
  // Options
  options={[
    { label: "Basic", value: "basic" },
    { label: "Pro", value: "pro" },
    { label: "Enterprise", value: "ent", disabled: true },
  ]}
  
  // Value
  value="pro"
  defaultValue="basic"
  
  // Validation
  required={true}
  
  // Styling
  boxBackground="#fff"         // Circle background color
  dotColor="#10b981"           // Selected dot color
  
  // State
  disabled={false}
  readOnly={false}
  
  // Callbacks
  onChange={(value) => console.log(value)}
  onValidate={(isValid, error) => console.log(isValid, error)}
/>
```

**Props:**
- `options` - Array of options: `{ label: string, value: string, disabled?: boolean }[]`
- `value`, `defaultValue` - Selected value (string)
- `required` - Must select one option
- `boxBackground` - Radio circle background color
- `dotColor` - Selected dot color
- `disabled`, `readOnly` - Disable interaction
- `onChange(value)` - Fired when selection changes (returns selected value or null)

---

### 12. Slider Input

Range slider with single or dual handles, formatting, and value bubble.

```tsx
import { SliderInput } from "@halo-widgets/react";

<SliderInput
  // Basic props
  label="Volume"
  helperText="Adjust volume level"
  
  // Mode
  mode="single"                // "single" | "range"
  
  // Range
  min={0}
  max={100}
  step={1}                     // Increment step
  precision={0}                // Decimal places
  
  // Value
  value={50}                   // For single mode
  value={[20, 80]}             // For range mode
  
  // Formatting
  prefix="$"                   // Prefix for display
  suffix="%"                   // Suffix for display
  showValueBubble={true}       // Show value above slider
  
  // State
  disabled={false}
  readOnly={false}
  
  // Callbacks
  onChange={(value) => console.log(value)}  // number or [number, number]
/>
```

**Props:**
- `mode` - "single" (one handle) or "range" (two handles)
- `min`, `max` - Slider range
- `step` - Increment amount per move
- `precision` - Decimal places for value
- `value` - Current value (number for single, tuple for range)
- `prefix`, `suffix` - Display formatting (e.g., "$", "%", "kg")
- `showValueBubble` - Display current value above slider
- `disabled`, `readOnly` - Disable interaction
- `onChange(value)` - Returns number (single) or [min, max] (range)

**Common use cases:**
```tsx
// Volume
<SliderInput min={0} max={100} suffix="%" />

// Price range
<SliderInput mode="range" min={0} max={1000} prefix="$" />

// Temperature
<SliderInput min={-20} max={40} suffix="°C" step={0.5} precision={1} />
```

---

## Styling

All widgets use CSS variables for theming. Override these in your global CSS:

```css
:root {
  --halo-color-primary: #6366f1;
  --halo-color-border: #e5e7eb;
  --halo-color-text: #111827;
  --halo-color-error: #ef4444;
  --halo-color-success: #10b981;
}
```

You can also apply custom classes via the `className` prop on any widget.

---

## License

MIT

---

## Support

For issues and feature requests, visit our [GitHub repository](https://github.com/your-org/halo-widgets).

