# ListBox

A versatile list component that supports filtering, lazy loading, single/multiple selection modes, and custom item rendering.

## Features

- **Multiple Selection Modes**: None, single, and multiple selection support
- **Search & Filter**: Built-in search functionality with custom filter functions
- **Lazy Loading**: Support for paginated data loading with infinite scroll
- **Custom Rendering**: Flexible item rendering with custom components
- **Interactive States**: Hover, selection, and click handling
- **Header/Footer Slots**: Customizable header and footer sections
- **Loading States**: Built-in loading indicators for async operations
- **Accessibility**: Keyboard navigation and screen reader support

## Installation

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

## Usage

### Basic Usage

```svelte
<script>
  import ListBox from '@ticatec/uniface-element/list-box';
  import MyItemComponent from './MyItemComponent.svelte';
  
  let items = [
    { id: 1, name: 'Item 1', description: 'First item' },
    { id: 2, name: 'Item 2', description: 'Second item' },
    { id: 3, name: 'Item 3', description: 'Third item' }
  ];
  
  let selectedItem = null;
</script>

<ListBox 
  list={items}
  itemRender={MyItemComponent}
  selectMode="single"
  bind:selectedItem
  style="width: 300px; height: 400px;"
/>
```

### Read-Only List Display

```svelte
<ListBox 
  list={items}
  itemRender={ItemRenderer}
  selectMode="none"
  readonly={true}
  style="width: 100%; height: 300px;"
/>
```

### Multiple Selection

```svelte
<script>
  let selectedList = [];
</script>

<ListBox 
  list={items}
  itemRender={ItemRenderer}
  selectMode="multiple"
  bind:selectedList
  title="Select Multiple Items"
  style="width: 100%; height: 400px;"
/>

<p>Selected: {selectedList.length} items</p>
```

### With Search/Filter

```svelte
<script>
  const filterFunction = (item, searchText) => {
    return item.name.toLowerCase().includes(searchText.toLowerCase()) ||
           item.description.toLowerCase().includes(searchText.toLowerCase());
  };
</script>

<ListBox 
  list={items}
  itemRender={ItemRenderer}
  selectMode="single"
  filter={filterFunction}
  title="Searchable List"
  bind:selectedItem
/>
```

### Lazy Loading

```svelte
<script>
  const lazyLoader = async (searchText, pageNo) => {
    const response = await fetch(`/api/items?search=${searchText}&page=${pageNo}`);
    const data = await response.json();
    
    return {
      list: data.items,
      hasMore: data.hasMore
    };
  };
</script>

<ListBox 
  lazyLoader={lazyLoader}
  itemRender={ItemRenderer}
  selectMode="single"
  bind:selectedItem
  style="width: 100%; height: 500px;"
/>
```

### Custom Header and Footer

```svelte
<ListBox 
  list={items}
  itemRender={ItemRenderer}
  selectMode="single"
  bind:selectedItem
>
  <div slot="header" style="padding: 12px; background: #f8f9fa; font-weight: 600;">
    Custom Header - Total: {items.length}
  </div>
  
  <div slot="footer" style="padding: 8px; text-align: right; background: #f8f9fa;">
    Selected: {selectedItem?.name || 'None'}
  </div>
</ListBox>
```

## API Reference

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `list` | `Array<any>` | `[]` | Array of items to display |
| `itemRender` | `SvelteComponent` | - | Component to render each item |
| `selectMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | Selection mode |
| `selectedItem` | `any` | `null` | Currently selected item (single mode) |
| `selectedList` | `Array<any>` | `[]` | Array of selected items (multiple mode) |
| `filter` | `FunFilter \| null` | `null` | Filter function for search |
| `lazyLoader` | `LazyLoader \| null` | `null` | Lazy loading function |
| `readonly` | `boolean` | `false` | Whether the list is read-only |
| `title` | `string` | - | Title text for the header |
| `style` | `string` | `''` | Custom CSS styles |
| `header$style` | `string` | `''` | Custom styles for header |
| `footer$style` | `string` | `''` | Custom styles for footer |
| `round` | `boolean` | `false` | Whether to use rounded corners |
| `item$props` | `any` | `null` | Additional props passed to item components |
| `class` | `string` | `''` | Additional CSS classes |

### Events

| Event | Type | Description |
|-------|------|-------------|
| `onSelectChange` | `(item: any) => void` | Fired when selection changes (single mode) |
| `onItemClick` | `(item: any) => void` | Fired when an item is clicked |
| `onItemDblClick` | `(item: any) => void` | Fired when an item is double-clicked |

### Slots

| Slot | Description |
|------|-------------|
| `header` | Custom header content |
| `footer` | Custom footer content |
| `loadMoreIndicator` | Custom loading indicator for lazy loading |

### Types

```typescript
// Filter function type
type FunFilter = (item: any, searchText: string) => boolean;

// Lazy loader function type
type LazyLoader = (searchText: string, pageNo: number) => Promise<LoadResult>;

// Load result interface
interface LoadResult {
  list: Array<any>;
  hasMore: boolean;
}
```

## Custom Item Renderer

Create a custom component to render list items:

```svelte
<!-- EmployeeRenderer.svelte -->
<script lang="ts">
  export let item;
  export let selected = false;
  export let readonly = false;
</script>

<div class="employee-item" class:selected>
  <div class="employee-name">{item.name}</div>
  <div class="employee-department">{item.department}</div>
  <div class="employee-phone">{item.phone}</div>
</div>

<style>
  .employee-item {
    padding: 12px;
    border-radius: 4px;
    transition: background-color 0.2s;
  }
  
  .employee-item.selected {
    background-color: #e3f2fd;
  }
  
  .employee-name {
    font-weight: 600;
    font-size: 1.1em;
    margin-bottom: 4px;
  }
  
  .employee-department {
    color: #666;
    font-size: 0.9em;
  }
  
  .employee-phone {
    color: #888;
    font-size: 0.8em;
    margin-top: 4px;
  }
</style>
```

## Examples

### Employee Directory

```svelte
<script>
  import ListBox from '@ticatec/uniface-element/list-box';
  import EmployeeRenderer from './EmployeeRenderer.svelte';
  
  let employees = [
    { id: 1, name: 'John Doe', department: 'Engineering', phone: '+1-555-0123' },
    { id: 2, name: 'Jane Smith', department: 'Marketing', phone: '+1-555-0124' },
    { id: 3, name: 'Bob Johnson', department: 'Sales', phone: '+1-555-0125' }
  ];
  
  let selectedEmployee = null;
  
  const filterEmployees = (employee, searchText) => {
    const text = searchText.toLowerCase();
    return employee.name.toLowerCase().includes(text) ||
           employee.department.toLowerCase().includes(text);
  };
  
  function handleEmployeeSelect(employee) {
    console.log('Selected employee:', employee);
  }
</script>

<ListBox 
  list={employees}
  itemRender={EmployeeRenderer}
  selectMode="single"
  filter={filterEmployees}
  bind:selectedItem={selectedEmployee}
  onSelectChange={handleEmployeeSelect}
  title="Employee Directory"
  style="width: 400px; height: 500px;"
>
  <div slot="footer" style="padding: 8px; text-align: center; background: #f5f5f5;">
    Total Employees: {employees.length}
    {#if selectedEmployee}
      | Selected: {selectedEmployee.name}
    {/if}
  </div>
</ListBox>
```

### Product Catalog with Lazy Loading

```svelte
<script>
  import ListBox from '@ticatec/uniface-element/list-box';
  import ProductRenderer from './ProductRenderer.svelte';
  
  let selectedProducts = [];
  
  const loadProducts = async (searchText, pageNo) => {
    const params = new URLSearchParams({
      search: searchText || '',
      page: pageNo.toString(),
      limit: '20'
    });
    
    try {
      const response = await fetch(`/api/products?${params}`);
      const data = await response.json();
      
      return {
        list: data.products,
        hasMore: data.totalPages > pageNo
      };
    } catch (error) {
      console.error('Failed to load products:', error);
      return { list: [], hasMore: false };
    }
  };
</script>

<ListBox 
  lazyLoader={loadProducts}
  itemRender={ProductRenderer}
  selectMode="multiple"
  bind:selectedList={selectedProducts}
  style="width: 100%; height: 600px;"
>
  <div slot="header" style="padding: 12px; background: #2196f3; color: white;">
    <h3 style="margin: 0;">Product Catalog</h3>
  </div>
  
  <div slot="footer" style="padding: 8px; background: #f0f0f0; text-align: right;">
    Selected: {selectedProducts.length} products
    <button 
      on:click={() => selectedProducts = []}
      disabled={selectedProducts.length === 0}
    >
      Clear Selection
    </button>
  </div>
  
  <div slot="loadMoreIndicator" style="padding: 16px; text-align: center;">
    <div class="loading-spinner"></div>
    Loading more products...
  </div>
</ListBox>
```

### Contact List with Actions

```svelte
<script>
  import ListBox from '@ticatec/uniface-element/list-box';
  import ContactRenderer from './ContactRenderer.svelte';
  
  let contacts = [
    { id: 1, name: 'Alice Brown', email: 'alice@example.com', phone: '+1-555-0001' },
    { id: 2, name: 'Charlie Davis', email: 'charlie@example.com', phone: '+1-555-0002' }
  ];
  
  let selectedContact = null;
  
  function handleContactDblClick(contact) {
    // Open contact details or edit modal
    console.log('Opening contact:', contact);
  }
  
  function handleContactCall(contact) {
    console.log('Calling:', contact.phone);
  }
  
  function handleContactEmail(contact) {
    window.location.href = `mailto:${contact.email}`;
  }
</script>

<ListBox 
  list={contacts}
  itemRender={ContactRenderer}
  selectMode="single"
  bind:selectedItem={selectedContact}
  onItemDblClick={handleContactDblClick}
  item$props={{ onCall: handleContactCall, onEmail: handleContactEmail }}
  title="Contacts"
  style="width: 350px; height: 450px;"
>
  <div slot="footer" style="padding: 12px; display: flex; gap: 8px;">
    <button 
      disabled={!selectedContact}
      on:click={() => handleContactCall(selectedContact)}
    >
      Call
    </button>
    <button 
      disabled={!selectedContact}
      on:click={() => handleContactEmail(selectedContact)}
    >
      Email
    </button>
  </div>
</ListBox>
```

## Styling

### CSS Variables

The ListBox component uses CSS variables for consistent theming:

```css
:root {
  --uniface-listbox-separator-color: #f0f0f0;
  --uniface-list-box-hover-bg: #ffffe1;
  --uniface-list-box-selected-bg: #b1c8f6;
  --uniface-list-box-header-bottom-border: 1px solid #f0f0f0;
  --uniface-list-box-border-color: #f0f0f0;
  --uniface-list-box-header-bg: #f8fafc;
  --uniface-list-box-footer-bg: #f8fafc;
}
```

### Custom Styling

```css
.custom-listbox {
  --uniface-list-box-hover-bg: #e3f2fd;
  --uniface-list-box-selected-bg: #1976d2;
  --uniface-list-box-border-color: #1976d2;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.custom-listbox .listview-item {
  transition: all 0.2s ease;
}

.custom-listbox .box-header {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}
```

## Accessibility Features

- **Keyboard Navigation**: Arrow keys for navigation, Enter for selection
- **Screen Reader Support**: Proper ARIA labels and roles
- **Focus Management**: Visible focus indicators and logical tab order
- **Selection Announcements**: Screen reader feedback for selection changes
- **Search Accessibility**: Proper labeling for search functionality

## Performance Considerations

### Large Lists

For large datasets, use lazy loading:

```svelte
<script>
  const lazyLoader = async (searchText, pageNo) => {
    // Implement efficient server-side pagination
    const response = await fetch(`/api/items?search=${searchText}&page=${pageNo}&limit=50`);
    return await response.json();
  };
</script>

<ListBox 
  lazyLoader={lazyLoader}
  itemRender={OptimizedItemRenderer}
  style="height: 400px;"
/>
```

### Virtual Scrolling

For extremely large lists, consider implementing virtual scrolling in your item renderer:

```svelte
<!-- VirtualizedItemRenderer.svelte -->
<script>
  export let item;
  export let index;
  
  // Implement virtual scrolling logic here
</script>
```

## Best Practices

### 1. Optimize Item Renderers

Keep item renderers simple and efficient:

```svelte
<!-- Good: Simple and efficient -->
<script>
  export let item;
</script>

<div class="item">
  <h4>{item.title}</h4>
  <p>{item.description}</p>
</div>

<!-- Avoid: Heavy computations in renderer -->
<script>
  export let item;
  
  // Don't do heavy processing here
  $: processedData = heavyComputation(item);
</script>
```

### 2. Implement Proper Loading States

```svelte
<ListBox 
  lazyLoader={dataLoader}
  itemRender={ItemRenderer}
>
  <div slot="loadMoreIndicator" class="loading-indicator">
    <div class="spinner"></div>
    Loading more items...
  </div>
</ListBox>
```

### 3. Handle Empty States

```svelte
<script>
  let items = [];
  let isLoading = false;
</script>

{#if isLoading}
  <div class="loading-state">Loading...</div>
{:else if items.length === 0}
  <div class="empty-state">No items found</div>
{:else}
  <ListBox list={items} itemRender={ItemRenderer} />
{/if}
```

### 4. Provide Clear Selection Feedback

```svelte
<ListBox 
  bind:selectedItem
  selectMode="single"
  style="--uniface-list-box-selected-bg: #2196f3; --uniface-list-box-hover-bg: #e3f2fd;"
/>

<div class="selection-info">
  {#if selectedItem}
    Selected: {selectedItem.name}
  {:else}
    No item selected
  {/if}
</div>
```

## Browser Support

- **Modern Browsers**: Chrome, Firefox, Safari, Edge (latest versions)
- **Mobile Browsers**: iOS Safari, Chrome Mobile, Firefox Mobile
- **Flexbox Support**: Required for layout functionality
- **Intersection Observer**: Required for lazy loading (polyfill available)

## Related Components

- **SearchBox**: Used internally for filtering functionality
- **CheckBox**: Used for multiple selection mode
- **Box**: Base layout component

## License

MIT License - see LICENSE file for details.