# OptionsSelect

A single-selection dropdown component that allows users to select one option from a predefined list with customizable styling and behavior.

## Features

- **Single Selection**: Select one option from a dropdown list
- **Custom Field Mapping**: Configure which object properties to use for keys and display text
- **Option Control**: Hide or disable specific options
- **Custom Rendering**: Use custom components to render dropdown items
- **Mandatory Mode**: Prevent clearing of selected values
- **Quick Filter**: Built-in search/filter functionality for large option lists
- **Focus Management**: Proper focus handling and keyboard navigation
- **Display Modes**: Support for edit and view modes
- **Empty State Handling**: Customizable placeholder and empty text
- **Event Callbacks**: Rich event handling for selection, focus, and blur

## Installation

```bash
npm install @ticatec/uniface-element
```

## Basic Usage

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';
  
  let selectedValue = null;
  let options = [
    { code: 'JS', text: 'JavaScript' },
    { code: 'TS', text: 'TypeScript' },
    { code: 'PY', text: 'Python' }
  ];
</script>

<OptionsSelect 
  {options} 
  bind:value={selectedValue}
/>
```

## API Reference

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `any` | `null` | Selected option key/value |
| `options` | `Array<any>` | `[]` | Array of option objects |
| `keyField` | `string` | `'code'` | Property name for option keys |
| `textField` | `string` | `'text'` | Property name for option display text |
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | Visual style variant |
| `compact` | `boolean` | `false` | Enables compact display mode |
| `disabled` | `boolean` | `false` | Disables the component |
| `readonly` | `boolean` | `false` | Makes the component read-only |
| `mandatory` | `boolean` | `false` | Prevents clearing selected value |
| `style` | `string` | `''` | Custom CSS styles |
| `placeholder` | `string` | `''` | Placeholder text when no option selected |
| `emptyText` | `string` | `null` | Text to show when no option selected (overrides placeholder) |
| `disableOptions` | `Array<string>` | `[]` | Keys of options to disable |
| `hideOptions` | `Array<string>` | `[]` | Keys of options to hide |
| `displayMode` | `DisplayMode` | `DisplayMode.Edit` | Edit or view mode |
| `menu$height` | `number` | `0` | Maximum height of dropdown menu |
| `itemRender` | `any` | `null` | Custom component for rendering items |
| `quickFilter` | `boolean` | `false` | Enable quick filter/search in dropdown |
| `quickFilterPlaceholder` | `string` | `'Search...'` | Placeholder text for search input |
| `filterFunction` | `(option: any, keyword: string) => boolean` | `null` | Custom filter function |
| `noResultsText` | `string` | `'No results found'` | Text when no options match filter |
| `onchange` | `OnChangeHandler<any>` | `null` | Change event handler |
| `onfocus` | `(() => void) \| null` | `null` | Focus event handler |
| `onblur` | `(() => void) \| null` | `null` | Blur event handler |
| `onSelected` | `OnSelectedHandler` | `null` | Selection event handler with full item data |

### Methods

| Method | Description |
|--------|-------------|
| `setFocus()` | Programmatically focus the component |

## Examples

### Currency Selector

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';
  import FormField from '@ticatec/uniface-element/FormField';
  
  let selectedCurrency = 'USD';
  let currencies = [
    { code: 'USD', text: 'US Dollar' },
    { code: 'EUR', text: 'Euro' },
    { code: 'GBP', text: 'British Pound' },
    { code: 'JPY', text: 'Japanese Yen' },
    { code: 'CNY', text: 'Chinese Yuan' }
  ];
  
  function handleCurrencyChange(currency) {
    console.log('Currency changed to:', currency);
  }
</script>

<FormField label="Currency">
  <OptionsSelect 
    options={currencies}
    bind:value={selectedCurrency}
    variant="outlined"
    placeholder="Select currency"
    onchange={handleCurrencyChange}
  />
</FormField>
```

### Country Selector with Custom Fields

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';
  
  let selectedCountry = null;
  let countries = [
    { id: 'US', name: 'United States' },
    { id: 'CA', name: 'Canada' },
    { id: 'UK', name: 'United Kingdom' },
    { id: 'DE', name: 'Germany' },
    { id: 'JP', name: 'Japan' }
  ];
  
  function handleCountrySelection(countryItem) {
    console.log('Selected country object:', countryItem);
  }
</script>

<OptionsSelect 
  options={countries}
  keyField="id"
  textField="name"
  bind:value={selectedCountry}
  variant="filled"
  placeholder="Choose your country"
  onSelected={handleCountrySelection}
/>
```

### Status Selector with Disabled Options

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';
  
  let taskStatus = 'PENDING';
  let statusOptions = [
    { code: 'PENDING', text: 'Pending' },
    { code: 'IN_PROGRESS', text: 'In Progress' },
    { code: 'REVIEW', text: 'Under Review' },
    { code: 'COMPLETED', text: 'Completed' },
    { code: 'CANCELLED', text: 'Cancelled' },
    { code: 'ARCHIVED', text: 'Archived' }
  ];
  
  let disabledStatuses = ['ARCHIVED']; // Archive status not available
  let hiddenStatuses = ['CANCELLED']; // Hide cancelled option
</script>

<div class="status-selector">
  <label>Task Status</label>
  <OptionsSelect 
    options={statusOptions}
    bind:value={taskStatus}
    variant="outlined"
    disableOptions={disabledStatuses}
    hideOptions={hiddenStatuses}
    mandatory
    emptyText="No status set"
  />
</div>
```

### Language Preference

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';
  
  let preferredLanguage = null;
  let languages = [
    { locale: 'en', display: 'English' },
    { locale: 'es', display: 'Espa�ol' },
    { locale: 'fr', display: 'Fran�ais' },
    { locale: 'de', display: 'Deutsch' },
    { locale: 'zh', display: '-�' },
    { locale: 'ja', display: '�,�' }
  ];
  
  function handleLanguageChange(locale) {
    console.log('Language changed to:', locale);
    // Update application language
    updateAppLanguage(locale);
  }
  
  function handleFocus() {
    console.log('Language selector focused');
  }
  
  function handleBlur() {
    console.log('Language selector blurred');
  }
</script>

<div class="language-settings">
  <OptionsSelect 
    options={languages}
    keyField="locale"
    textField="display"
    bind:value={preferredLanguage}
    variant="filled"
    placeholder="Select your language"
    onchange={handleLanguageChange}
    onfocus={handleFocus}
    onblur={handleBlur}
  />
</div>
```

### Priority Selector with Custom Styling

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';
  import PriorityItem from './PriorityItem.svelte'; // Custom component
  
  let priority = 'MEDIUM';
  let priorities = [
    { level: 'LOW', label: 'Low Priority', color: '#4caf50' },
    { level: 'MEDIUM', label: 'Medium Priority', color: '#ff9800' },
    { level: 'HIGH', label: 'High Priority', color: '#f44336' },
    { level: 'URGENT', label: 'Urgent', color: '#9c27b0' }
  ];
  
  const priorityRenderer = {
    component: PriorityItem,
    props: {
      showIcon: true
    }
  };
</script>

<div class="priority-selector">
  <label>Task Priority</label>
  <OptionsSelect 
    options={priorities}
    keyField="level"
    textField="label"
    bind:value={priority}
    variant="outlined"
    itemRender={priorityRenderer}
    mandatory
  />
</div>
```

### Theme Selector

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';
  
  let currentTheme = 'light';
  let themes = [
    { code: 'light', text: 'Light Theme' },
    { code: 'dark', text: 'Dark Theme' },
    { code: 'auto', text: 'System Default' },
    { code: 'high-contrast', text: 'High Contrast' }
  ];
  
  function applyTheme(themeCode) {
    console.log('Applying theme:', themeCode);
    document.documentElement.setAttribute('data-theme', themeCode);
  }
</script>

<div class="theme-settings">
  <h3>Appearance</h3>
  <OptionsSelect 
    options={themes}
    bind:value={currentTheme}
    variant="outlined"
    mandatory
    onchange={applyTheme}
    style="width: 200px;"
  />
</div>
```

### Form with Multiple Selectors

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';
  import FormField from '@ticatec/uniface-element/FormField';
  
  let userProfile = {
    gender: null,
    country: null,
    timezone: null,
    currency: 'USD'
  };
  
  let genders = [
    { code: 'M', text: 'Male' },
    { code: 'F', text: 'Female' },
    { code: 'O', text: 'Other' },
    { code: 'N', text: 'Prefer not to say' }
  ];
  
  let countries = [
    { id: 'US', name: 'United States' },
    { id: 'CA', name: 'Canada' },
    { id: 'UK', name: 'United Kingdom' },
    { id: 'AU', name: 'Australia' }
  ];
  
  let timezones = [
    { code: 'UTC', text: 'UTC' },
    { code: 'EST', text: 'Eastern Time' },
    { code: 'PST', text: 'Pacific Time' },
    { code: 'GMT', text: 'Greenwich Mean Time' }
  ];
  
  let currencies = [
    { code: 'USD', text: 'US Dollar' },
    { code: 'EUR', text: 'Euro' },
    { code: 'GBP', text: 'British Pound' },
    { code: 'CAD', text: 'Canadian Dollar' }
  ];
  
  function saveProfile() {
    console.log('Saving profile:', userProfile);
  }
</script>

<div class="profile-form">
  <h3>User Profile</h3>
  
  <FormField label="Gender">
    <OptionsSelect 
      options={genders}
      bind:value={userProfile.gender}
      variant="outlined"
      placeholder="Select gender"
    />
  </FormField>
  
  <FormField label="Country">
    <OptionsSelect 
      options={countries}
      keyField="id"
      textField="name"
      bind:value={userProfile.country}
      variant="outlined"
      placeholder="Select country"
      mandatory
    />
  </FormField>
  
  <FormField label="Timezone">
    <OptionsSelect 
      options={timezones}
      bind:value={userProfile.timezone}
      variant="outlined"
      placeholder="Select timezone"
    />
  </FormField>
  
  <FormField label="Currency">
    <OptionsSelect 
      options={currencies}
      bind:value={userProfile.currency}
      variant="outlined"
      mandatory
    />
  </FormField>
  
  <button on:click={saveProfile}>Save Profile</button>
</div>
```

### Compact Table Usage

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';
  
  let statusOptions = [
    { code: 'ACTIVE', text: 'Active' },
    { code: 'INACTIVE', text: 'Inactive' },
    { code: 'PENDING', text: 'Pending' }
  ];
  
  let users = [
    { id: 1, name: 'John Doe', status: 'ACTIVE' },
    { id: 2, name: 'Jane Smith', status: 'INACTIVE' },
    { id: 3, name: 'Bob Wilson', status: 'PENDING' }
  ];
  
  function updateUserStatus(userId, newStatus) {
    const user = users.find(u => u.id === userId);
    if (user) {
      user.status = newStatus;
      users = [...users];
      console.log(`User ${user.name} status updated to ${newStatus}`);
    }
  }
</script>

<table class="user-table">
  <thead>
    <tr>
      <th>Name</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody>
    {#each users as user}
      <tr>
        <td>{user.name}</td>
        <td>
          <OptionsSelect 
            options={statusOptions}
            value={user.status}
            compact
            variant="outlined"
            onchange={(status) => updateUserStatus(user.id, status)}
          />
        </td>
      </tr>
    {/each}
  </tbody>
</table>
```

### Quick Filter for Large Lists

When dealing with many options, enable the built-in quick filter:

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';

  let selectedCountry = null;
  let countries = [
    { code: 'US', name: 'United States' },
    { code: 'CA', name: 'Canada' },
    { code: 'UK', name: 'United Kingdom' },
    { code: 'DE', name: 'Germany' },
    { code: 'FR', name: 'France' },
    { code: 'JP', name: 'Japan' },
    { code: 'CN', name: 'China' },
    { code: 'AU', name: 'Australia' },
    // ... many more countries
  ];
</script>

<OptionsSelect
  options={countries}
  keyField="code"
  textField="name"
  bind:value={selectedCountry}
  variant="outlined"
  placeholder="Select country"
  quickFilter={true}
  quickFilterPlaceholder="Type to search countries..."
/>
```

### Custom Filter Function

Provide a custom filter function for advanced filtering:

```svelte
<script>
  import OptionsSelect from '@ticatec/uniface-element/OptionsSelect';

  let selectedProduct = null;
  let products = [
    { sku: 'LAPTOP-001', name: 'Laptop', category: 'Electronics' },
    { sku: 'PHONE-002', name: 'Smartphone', category: 'Electronics' },
    { sku: 'SHIRT-003', name: 'T-Shirt', category: 'Clothing' },
    { sku: 'PANTS-004', name: 'Jeans', category: 'Clothing' }
  ];

  const productFilter = (option, keyword) => {
    if (!keyword) return true;
    const search = keyword.toLowerCase();
    return option.name.toLowerCase().includes(search) ||
           option.sku.toLowerCase().includes(search) ||
           option.category.toLowerCase().includes(search);
  };
</script>

<OptionsSelect
  options={products}
  keyField="sku"
  textField="name"
  bind:value={selectedProduct}
  quickFilter={true}
  filterFunction={productFilter}
  quickFilterPlaceholder="Search products..."
  noResultsText="No matching products found"
/>
```

### Dynamic Options Loading
  
  let selectedCategory = null;
  let selectedProduct = null;
  let categories = [
    { id: 'electronics', name: 'Electronics' },
    { id: 'clothing', name: 'Clothing' },
    { id: 'books', name: 'Books' }
  ];
  let products = [];
  
  async function loadProducts(categoryId) {
    if (!categoryId) {
      products = [];
      selectedProduct = null;
      return;
    }
    
    // Simulate API call
    const productData = {
      'electronics': [
        { sku: 'E001', name: 'Laptop' },
        { sku: 'E002', name: 'Smartphone' }
      ],
      'clothing': [
        { sku: 'C001', name: 'T-Shirt' },
        { sku: 'C002', name: 'Jeans' }
      ],
      'books': [
        { sku: 'B001', name: 'Programming Guide' },
        { sku: 'B002', name: 'Design Patterns' }
      ]
    };
    
    products = productData[categoryId] || [];
    selectedProduct = null;
  }
  
  $: loadProducts(selectedCategory);
</script>

<div class="product-selector">
  <div class="selector-row">
    <label>Category</label>
    <OptionsSelect 
      options={categories}
      keyField="id"
      textField="name"
      bind:value={selectedCategory}
      variant="outlined"
      placeholder="Select category"
    />
  </div>
  
  <div class="selector-row">
    <label>Product</label>
    <OptionsSelect 
      options={products}
      keyField="sku"
      textField="name"
      bind:value={selectedProduct}
      variant="outlined"
      placeholder={selectedCategory ? 'Select product' : 'Select category first'}
      disabled={!selectedCategory}
    />
  </div>
</div>
```

## Custom Item Rendering

You can provide a custom component to render dropdown items:

```svelte
<!-- CustomItemRenderer.svelte -->
<script>
  export let item;
  export let disabled = false;
  
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();
  
  function handleClick() {
    if (!disabled) {
      dispatch('click');
    }
  }
</script>

<div class="custom-item" class:disabled on:click={handleClick}>
  <div class="item-icon"><�</div>
  <div class="item-content">
    <div class="item-title">{item.title}</div>
    <div class="item-description">{item.description}</div>
  </div>
</div>

<style>
  .custom-item {
    display: flex;
    align-items: center;
    padding: 12px;
    cursor: pointer;
    border-bottom: 1px solid #eee;
  }
  
  .custom-item:hover:not(.disabled) {
    background-color: #f5f5f5;
  }
  
  .custom-item.disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
  
  .item-icon {
    margin-right: 12px;
    font-size: 18px;
  }
  
  .item-title {
    font-weight: 600;
    margin-bottom: 4px;
  }
  
  .item-description {
    font-size: 12px;
    color: #666;
  }
</style>
```

## Best Practices

1. **Option Management**: Keep option lists focused and relevant
2. **Performance**: Consider virtualizing for very large option lists
3. **Accessibility**: Provide clear labels and meaningful option text
4. **User Experience**: Use appropriate variants and sizing for your design
5. **Data Validation**: Handle edge cases like missing options
6. **Loading States**: Show loading indicators for dynamic options
7. **Error Handling**: Gracefully handle API failures for dynamic options

## Styling

```css
.options-popover {
  max-height: 300px;
  overflow-y: auto;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
  background: white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.option-item {
  padding: 8px 12px;
  cursor: pointer;
  border-bottom: 1px solid #f0f0f0;
}

.option-item:hover:not(.disabled) {
  background-color: #f5f5f5;
}

.option-item.disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background-color: #fafafa;
}

.option-item:last-child {
  border-bottom: none;
}
```

## Accessibility

- Full keyboard navigation support (Arrow keys, Enter, Escape)
- ARIA labels and roles for screen readers
- Focus management and visual indicators
- High contrast mode compatibility
- Proper labeling for form associations

## Related Components

- [OptionsMultiSelect](../options-multi-select/README.md) - For multiple option selection
- [FormField](../form-field/README.md) - For form layout and labels
- [CommonPicker](../common/README.md) - Base picker functionality
- [LookupEditor](../lookup-editor/README.md) - For dynamic data lookup