# CascadeOptionSelect Component

A hierarchical dropdown component for selecting options from nested data structures. Perfect for location selectors (country/state/city), category hierarchies, organizational structures, and any multi-level selection scenarios.

## Features

- **Hierarchical Selection**: Navigate through nested option levels
- **Dynamic Loading**: Async loading of child options via callbacks
- **Flexible Data Structure**: Configurable field mapping for different data formats
- **Leaf Detection**: Custom logic to determine selectable end nodes
- **Display Modes**: Edit and display modes for different UI contexts
- **Keyboard & Focus Support**: Full keyboard navigation and focus management
- **Loading States**: Built-in loading indicators for async operations
- **Customizable Styling**: Flexible appearance options and variants
- **Clean/Mandatory Options**: Optional clear functionality and required selection

## Basic Usage

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

const locationData = [
  {
    code: 'US',
    text: 'United States',
    abbr: 'US',
    children: [
      {
        code: 'CA',
        text: 'California',
        abbr: 'CA',
        children: [
          { code: 'SF', text: 'San Francisco', abbr: 'SF' },
          { code: 'LA', text: 'Los Angeles', abbr: 'LA' }
        ]
      }
    ]
  }
];

let selectedValue = null;

const handleChange = (value) => {
  console.log('Selected:', value);
  selectedValue = value;
};
</script>

<CascadeOptionSelect 
  nodes={locationData}
  bind:value={selectedValue}
  onchange={handleChange}
  placeholder="Select location..."
/>
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | Visual variant style |
| `disabled` | `boolean` | `false` | Whether the component is disabled |
| `readonly` | `boolean` | `false` | Whether the component is read-only |
| `compact` | `boolean` | `false` | Compact display mode |
| `mandatory` | `boolean` | `false` | Whether selection is required (disables clear) |
| `value` | `any` | `null` | Selected value (key field value) |
| `keyField` | `string` | `'code'` | Field name for option keys/values |
| `textField` | `string` | `'text'` | Field name for option display text |
| `abbrField` | `string` | `'abbr'` | Field name for abbreviated text in dropdown |
| `childrenField` | `string` | `'children'` | Field name for child options |
| `style` | `string` | `''` | Custom CSS styles |
| `placeholder` | `string` | `''` | Placeholder text |
| `displayMode` | `DisplayMode` | `DisplayMode.Edit` | Display or edit mode |
| `emptyText` | `string \| null` | `null` | Text to show when no selection |
| `checkLeaf` | `IsLeafDetermine \| null` | `null` | Function to determine if item is selectable leaf |
| `text` | `string` | `''` | Display text (automatically managed) |
| `nodes` | `Array<any>` | `[]` | Root level options array |
| `menu$height` | `number` | `150` | Dropdown menu height in pixels |
| `onSelect` | `OnSelectOption` | - | Async function to load child options |
| `box$style` | `string` | `''` | Custom styles for option boxes |
| `onchange` | `OnChangeHandler<any>` | - | Change event handler |
| `onfocus` | `(() => void) \| null` | `null` | Focus event handler |
| `onblur` | `(() => void) \| null` | `null` | Blur event handler |

## Type Definitions

```typescript
// Function to load child options asynchronously
type OnSelectOption = (item: any) => Promise<Array<any>>;

// Function to determine if an item is a selectable leaf node
type IsLeafDetermine = (item: any) => boolean;

// Change event handler
type OnChangeHandler<T> = (value: T) => void;
```

## Static Data Example

```svelte
<script>
const categories = [
  {
    code: 'electronics',
    text: 'Electronics',
    abbr: 'Electronics',
    children: [
      {
        code: 'computers',
        text: 'Computers',
        abbr: 'Computers',
        children: [
          { code: 'laptops', text: 'Laptops', abbr: 'Laptops' },
          { code: 'desktops', text: 'Desktops', abbr: 'Desktops' }
        ]
      },
      {
        code: 'phones',
        text: 'Mobile Phones',
        abbr: 'Phones',
        children: [
          { code: 'smartphones', text: 'Smartphones', abbr: 'Smartphones' },
          { code: 'basic-phones', text: 'Basic Phones', abbr: 'Basic' }
        ]
      }
    ]
  }
];

let selectedCategory = null;
</script>

<CascadeOptionSelect 
  nodes={categories}
  bind:value={selectedCategory}
  placeholder="Select category..."
  onchange={(value) => console.log('Selected category:', value)}
/>
```

## Dynamic Loading Example

```svelte
<script>
const loadRegions = async (country) => {
  // Simulate API call
  const response = await fetch(`/api/regions?country=${country.code}`);
  return await response.json();
};

const loadCities = async (region) => {
  const response = await fetch(`/api/cities?region=${region.code}`);
  return await response.json();
};

const dynamicLoader = async (item) => {
  // Determine what type of data to load based on the item
  if (!item.children) {
    if (item.type === 'country') {
      return await loadRegions(item);
    } else if (item.type === 'region') {
      return await loadCities(item);
    }
  }
  return item.children || [];
};

const countries = [
  { code: 'US', text: 'United States', abbr: 'US', type: 'country' },
  { code: 'CA', text: 'Canada', abbr: 'CA', type: 'country' },
  { code: 'UK', text: 'United Kingdom', abbr: 'UK', type: 'country' }
];

let selectedLocation = null;
</script>

<CascadeOptionSelect 
  nodes={countries}
  bind:value={selectedLocation}
  onSelect={dynamicLoader}
  placeholder="Select location..."
  onchange={(value) => console.log('Selected location:', value)}
/>
```

## Custom Field Mapping

```svelte
<script>
// Data with different field names
const orgData = [
  {
    id: 'corp',
    name: 'Corporation',
    short: 'Corp',
    divisions: [
      {
        id: 'tech',
        name: 'Technology Division',
        short: 'Tech',
        departments: [
          { id: 'dev', name: 'Development', short: 'Dev' },
          { id: 'qa', name: 'Quality Assurance', short: 'QA' }
        ]
      }
    ]
  }
];

let selectedOrg = null;
</script>

<CascadeOptionSelect 
  nodes={orgData}
  bind:value={selectedOrg}
  keyField="id"
  textField="name"
  abbrField="short"
  childrenField="divisions"
  placeholder="Select organization..."
/>
```

## Leaf Detection Example

```svelte
<script>
const menuData = [
  {
    code: 'file',
    text: 'File',
    abbr: 'File',
    isLeaf: false,
    children: [
      { code: 'new', text: 'New File', abbr: 'New', isLeaf: true },
      { code: 'open', text: 'Open File', abbr: 'Open', isLeaf: true },
      { code: 'save', text: 'Save File', abbr: 'Save', isLeaf: true }
    ]
  }
];

const checkIfLeaf = (item) => {
  return item.isLeaf === true;
};

let selectedMenuItem = null;
</script>

<CascadeOptionSelect 
  nodes={menuData}
  bind:value={selectedMenuItem}
  checkLeaf={checkIfLeaf}
  placeholder="Select menu item..."
/>
```

## Different Variants

```svelte
<!-- Default variant -->
<CascadeOptionSelect 
  nodes={data}
  placeholder="Default style"
/>

<!-- Plain variant -->
<CascadeOptionSelect 
  nodes={data}
  variant="plain"
  placeholder="Plain style"
/>

<!-- Outlined variant -->
<CascadeOptionSelect 
  nodes={data}
  variant="outlined"
  placeholder="Outlined style"
/>

<!-- Filled variant -->
<CascadeOptionSelect 
  nodes={data}
  variant="filled"
  placeholder="Filled style"
/>
```

## Event Handling

```svelte
<script>
let currentValue = null;
let focusCount = 0;

const handleChange = (value) => {
  console.log('Value changed to:', value);
  currentValue = value;
};

const handleFocus = () => {
  console.log('Component focused');
  focusCount++;
};

const handleBlur = () => {
  console.log('Component blurred');
};
</script>

<CascadeOptionSelect 
  nodes={data}
  bind:value={currentValue}
  onchange={handleChange}
  onfocus={handleFocus}
  onblur={handleBlur}
  placeholder="Event handling example"
/>

<p>Current value: {currentValue}</p>
<p>Focus count: {focusCount}</p>
```

## Advanced Examples

### Location Selector with API Integration

```svelte
<script>
import { onMount } from 'svelte';

let countries = [];
let selectedLocation = null;

onMount(async () => {
  // Load initial countries
  const response = await fetch('/api/countries');
  countries = await response.json();
});

const loadLocationChildren = async (location) => {
  let endpoint = '';
  
  if (location.type === 'country') {
    endpoint = `/api/states?country=${location.code}`;
  } else if (location.type === 'state') {
    endpoint = `/api/cities?state=${location.code}`;
  }
  
  if (endpoint) {
    const response = await fetch(endpoint);
    return await response.json();
  }
  
  return [];
};

const isLocationLeaf = (location) => {
  return location.type === 'city';
};
</script>

<div class="location-selector">
  <label>Select Location:</label>
  <CascadeOptionSelect 
    nodes={countries}
    bind:value={selectedLocation}
    onSelect={loadLocationChildren}
    checkLeaf={isLocationLeaf}
    placeholder="Country / State / City"
    mandatory={true}
    variant="outlined"
  />
</div>
```

### Product Category Navigator

```svelte
<script>
const productCategories = [
  {
    code: 'electronics',
    text: 'Electronics',
    abbr: 'Electronics',
    children: [
      {
        code: 'computers',
        text: 'Computers & Accessories',
        abbr: 'Computers',
        children: [
          { code: 'laptops', text: 'Laptops', abbr: 'Laptops' },
          { code: 'desktops', text: 'Desktop Computers', abbr: 'Desktops' },
          { code: 'accessories', text: 'Computer Accessories', abbr: 'Accessories' }
        ]
      },
      {
        code: 'mobile',
        text: 'Mobile Devices',
        abbr: 'Mobile',
        children: [
          { code: 'smartphones', text: 'Smartphones', abbr: 'Phones' },
          { code: 'tablets', text: 'Tablets', abbr: 'Tablets' },
          { code: 'wearables', text: 'Wearable Technology', abbr: 'Wearables' }
        ]
      }
    ]
  },
  {
    code: 'clothing',
    text: 'Clothing & Fashion',
    abbr: 'Clothing',
    children: [
      {
        code: 'mens',
        text: "Men's Clothing",
        abbr: "Men's",
        children: [
          { code: 'shirts', text: 'Shirts', abbr: 'Shirts' },
          { code: 'pants', text: 'Pants', abbr: 'Pants' },
          { code: 'shoes', text: 'Shoes', abbr: 'Shoes' }
        ]
      }
    ]
  }
];

let selectedCategory = null;

$: if (selectedCategory) {
  console.log('Browse products in category:', selectedCategory);
}
</script>

<div class="category-navigator">
  <h3>Browse Products</h3>
  <CascadeOptionSelect 
    nodes={productCategories}
    bind:value={selectedCategory}
    placeholder="Select category to browse..."
    variant="filled"
    menu$height={200}
  />
</div>
```

### Organization Hierarchy Selector

```svelte
<script>
const organizationData = [
  {
    code: 'headquarters',
    text: 'Headquarters',
    abbr: 'HQ',
    children: [
      {
        code: 'exec',
        text: 'Executive',
        abbr: 'Exec',
        children: [
          { code: 'ceo', text: 'Chief Executive Officer', abbr: 'CEO' },
          { code: 'cto', text: 'Chief Technology Officer', abbr: 'CTO' }
        ]
      },
      {
        code: 'engineering',
        text: 'Engineering',
        abbr: 'Eng',
        children: [
          { code: 'frontend', text: 'Frontend Development', abbr: 'Frontend' },
          { code: 'backend', text: 'Backend Development', abbr: 'Backend' },
          { code: 'devops', text: 'DevOps', abbr: 'DevOps' }
        ]
      }
    ]
  }
];

let assignedDepartment = null;

const handleDepartmentChange = (value) => {
  console.log('Assigned to department:', value);
  // Update user assignment
};
</script>

<div class="department-assignment">
  <label>Assign to Department:</label>
  <CascadeOptionSelect 
    nodes={organizationData}
    bind:value={assignedDepartment}
    onchange={handleDepartmentChange}
    placeholder="Select department..."
    mandatory={true}
    compact={true}
  />
</div>
```

### Menu System with Dynamic Loading

```svelte
<script>
let menuItems = [
  { code: 'file', text: 'File', abbr: 'File', hasChildren: true },
  { code: 'edit', text: 'Edit', abbr: 'Edit', hasChildren: true },
  { code: 'view', text: 'View', abbr: 'View', hasChildren: true }
];

const loadMenuChildren = async (menuItem) => {
  // Simulate loading menu items from server
  await new Promise(resolve => setTimeout(resolve, 500));
  
  const menuMap = {
    'file': [
      { code: 'new', text: 'New', abbr: 'New', action: 'file.new' },
      { code: 'open', text: 'Open', abbr: 'Open', action: 'file.open' },
      { code: 'save', text: 'Save', abbr: 'Save', action: 'file.save' }
    ],
    'edit': [
      { code: 'cut', text: 'Cut', abbr: 'Cut', action: 'edit.cut' },
      { code: 'copy', text: 'Copy', abbr: 'Copy', action: 'edit.copy' },
      { code: 'paste', text: 'Paste', abbr: 'Paste', action: 'edit.paste' }
    ],
    'view': [
      { code: 'zoom-in', text: 'Zoom In', abbr: 'Zoom+', action: 'view.zoomIn' },
      { code: 'zoom-out', text: 'Zoom Out', abbr: 'Zoom-', action: 'view.zoomOut' }
    ]
  };
  
  return menuMap[menuItem.code] || [];
};

const isMenuItem = (item) => {
  return item.action != null;
};

let selectedAction = null;

$: if (selectedAction) {
  console.log('Execute action:', selectedAction);
}
</script>

<CascadeOptionSelect 
  nodes={menuItems}
  bind:value={selectedAction}
  onSelect={loadMenuChildren}
  checkLeaf={isMenuItem}
  placeholder="Select action..."
  variant="outlined"
/>
```

## Styling

The component uses the following CSS classes and can be styled through:

- Custom `style` prop for the main container
- `box$style` prop for individual option boxes
- `menu$height` prop for dropdown height
- CSS custom properties for deeper customization

### Example Styling

```svelte
<CascadeOptionSelect 
  nodes={data}
  style="border-radius: 8px; border: 2px solid #007acc;"
  box$style="background: #f5f5f5; border-radius: 4px;"
  menu$height={250}
/>
```

## Accessibility

The component includes:

- Proper focus management
- Keyboard navigation support
- ARIA attributes for screen readers
- Focus and blur event handling
- Clear visual indicators for selected items

## Best Practices

1. **Data Structure**: Use consistent field naming across all levels
2. **Loading States**: Provide feedback during async operations
3. **Error Handling**: Handle API failures gracefully in onSelect callbacks
4. **Performance**: Consider virtualization for very large datasets
5. **Validation**: Validate leaf selection when mandatory is true
6. **Responsive Design**: Test dropdown behavior on mobile devices
7. **User Experience**: Provide clear visual hierarchy in option display

## Common Patterns

### Form Integration

```svelte
<script>
let formData = {
  location: null,
  category: null
};

const validateForm = () => {
  return formData.location && formData.category;
};
</script>

<form>
  <div class="form-field">
    <label>Location:</label>
    <CascadeOptionSelect 
      nodes={locationData}
      bind:value={formData.location}
      mandatory={true}
      placeholder="Select location..."
    />
  </div>
  
  <button type="submit" disabled={!validateForm()}>
    Submit
  </button>
</form>
```

### State Management

```svelte
<script>
import { writable } from 'svelte/store';

const selectedValues = writable({
  location: null,
  category: null,
  department: null
});

$: console.log('Current selections:', $selectedValues);
</script>

<CascadeOptionSelect 
  nodes={locationData}
  value={$selectedValues.location}
  onchange={(value) => selectedValues.update(s => ({...s, location: value}))}
/>
```

## Browser Support

Works in all modern browsers that support:
- ES6+ JavaScript
- CSS Flexbox
- Modern DOM APIs
- Promises/async-await

## Performance Considerations

- Efficient handling of large datasets through virtual scrolling in option boxes
- Debounced async loading to prevent excessive API calls
- Proper cleanup of event listeners and timeouts
- Optimized re-rendering with Svelte's reactivity system