npm package: @ng-shangjc/select
Description: Input component source package for ng-shangjc angular components
Author: jc-develop
Latest version: 1.0.3-beta

<p align="center">
<a href="https://juliancallejas.github.io/ng-shangjc-docs">
  <img src="https://res.cloudinary.com/dphleqb5t/image/upload/v1772819746/shangjc/shangjc-64-bg-dark_kwzs81.svg" width="307" alt="Shangjc"  /> 
</a>
</p>

# Select Component

A flexible and powerful select dropdown component with search functionality, keyboard navigation, and comprehensive form integration. Features both single and multi-select modes with full accessibility support and customizable styling options.

## Official Documentation

<a href="https://juliancallejas.github.io/ng-shangjc-docs/?path=/docs/components-select">
    ng-Shangjc docs
</a>

## Features

- 🔍 **Searchable**: Optional search functionality with debounced input to filter through options
- ⌨️ **Keyboard Navigation**: Full keyboard support with arrow keys, Enter, Space, and Escape
- ♿ **Accessibility**: ARIA attributes, proper roles, and screen reader support
- 🎯 **Type-Safe**: Full TypeScript support with comprehensive interfaces
- 📝 **Form Integration**: Implements ControlValueAccessor for reactive forms
- 🎨 **Customizable**: Multiple sizes, custom classes, and styling options
- 🚫 **Disabled States**: Support for disabled options and component
- 🔄 **Multi-Select**: Optional multiple selection mode with tag display
- ⚡ **Performance**: Optimized rendering with computed signals and debounced search

## Installation

### Step 1: Install the CLI

First, install the ng-shangjc CLI globally or use npx:

```bash
# Install globally
npm install -g @ng-shangjc/cli

# or with yarn
yarn global add @ng-shangjc/cli

# or with pnpm
pnpm install -g @ng-shangjc/cli

# Or use npx without installation
npx @ng-shangjc/cli <command>
```

### Step 2: Initialize your project

If you haven't already, initialize your Angular project for ng-shangjc components:

```bash
ng-shangjc init
```

This will create a `shangjc.config.json` file and set up your project for component installations.

### Step 3: Install the component

Install the select component into your project:

```bash
ng-shangjc install select
```

The component will be installed in the configured path (default: `src/ui/shangjc`). Adjust the import path in your components based on your project structure and the configured installation path.

## Import

After installation, import the components from your configured installation path. The examples below use the default path `src/ui/shangjc` - adjust the path according to your project structure and configuration.

### Standalone Components (Recommended)

Import and use the individual components directly in your standalone components:

```typescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { 
  SelectComponent,
  SelectTriggerComponent,
  SelectItemComponent,
  SelectOption
} from './ui/shangjc/select';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [
    CommonModule,
    SelectComponent,
    SelectTriggerComponent,
    SelectItemComponent
  ],
  template: `
    <ng-shangjc-select>
      <ng-shangjc-select-trigger placeholder="Choose an option"></ng-shangjc-select-trigger>
    </ng-shangjc-select>
  `
})
export class ExampleComponent { }
```

### Using NgModule (Legacy)

If you're using NgModules, import the `SelectModule`:

```typescript
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SelectModule } from './ui/shangjc/select';

@NgModule({
  declarations: [YourComponent],
  imports: [
    CommonModule,
    SelectModule
  ]
})
export class YourModule { }
```

Then use the components in your templates:

```html
<ng-shangjc-select>
  <ng-shangjc-select-trigger placeholder="Choose an option"></ng-shangjc-select-trigger>
</ng-shangjc-select>
```

## Basic Usage

### Default Example

```typescript
import { Component } from '@angular/core';
import { 
  SelectComponent,
  SelectTriggerComponent,
  SelectItemComponent,
  SelectOption
} from './ui/shangjc/select';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [
    SelectComponent,
    SelectTriggerComponent,
    SelectItemComponent
  ],
  template: `
    <ng-shangjc-select [options]="options" (selectionChange)="onSelectionChange($event)">
      <ng-shangjc-select-trigger placeholder="Select a fruit"></ng-shangjc-select-trigger>
    </ng-shangjc-select>
  `
})
export class ExampleComponent {
  options: SelectOption[] = [
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'orange', label: 'Orange' },
    { value: 'grape', label: 'Grape', disabled: true }
  ];

  onSelectionChange(option: SelectOption | null) {
    console.log('Selected:', option);
  }
}
```

### With Search

```typescript
<ng-shangjc-select [options]="options" [searchable]="true">
  <ng-shangjc-select-trigger placeholder="Search and select..."></ng-shangjc-select-trigger>
</ng-shangjc-select>
```

### Multi-Select

```typescript
<ng-shangjc-select [options]="options" [multiple]="true">
  <ng-shangjc-select-trigger placeholder="Select multiple fruits"></ng-shangjc-select-trigger>
</ng-shangjc-select>
```

### With Custom Items

```typescript
<ng-shangjc-select [options]="options">
  <ng-shangjc-select-trigger placeholder="Select or add custom"></ng-shangjc-select-trigger>
  <ng-shangjc-select-item value="custom" label="Custom Option"></ng-shangjc-select-item>
  <ng-shangjc-select-item value="another" label="Another Custom Option"></ng-shangjc-select-item>
</ng-shangjc-select>
```

### Different Sizes

```typescript
<ng-shangjc-select [options]="options">
  <ng-shangjc-select-trigger size="sm" placeholder="Small"></ng-shangjc-select-trigger>
</ng-shangjc-select>

<ng-shangjc-select [options]="options">
  <ng-shangjc-select-trigger size="default" placeholder="Default"></ng-shangjc-select-trigger>
</ng-shangjc-select>

<ng-shangjc-select [options]="options">
  <ng-shangjc-select-trigger size="lg" placeholder="Large"></ng-shangjc-select-trigger>
</ng-shangjc-select>
```

## Advanced Usage

### Reactive Forms Integration

```typescript
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { 
  SelectComponent,
  SelectTriggerComponent,
  SelectOption
} from './ui/shangjc/select';

@Component({
  selector: 'app-form-example',
  standalone: true,
  imports: [
    ReactiveFormsModule,
    SelectComponent,
    SelectTriggerComponent
  ],
  template: `
    <ng-shangjc-select [options]="options" [formControl]="fruitControl">
      <ng-shangjc-select-trigger placeholder="Select a fruit"></ng-shangjc-select-trigger>
    </ng-shangjc-select>
    
    <p>Selected value: {{ fruitControl.value }}</p>
  `
})
export class FormExampleComponent {
  fruitControl = new FormControl('apple');
  
  options: SelectOption[] = [
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'orange', label: 'Orange' }
  ];
}
```

### Multi-Select with Reactive Forms

```typescript
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { 
  SelectComponent,
  SelectTriggerComponent,
  SelectOption
} from './ui/shangjc/select';

@Component({
  selector: 'app-multi-form-example',
  standalone: true,
  imports: [
    ReactiveFormsModule,
    SelectComponent,
    SelectTriggerComponent
  ],
  template: `
    <ng-shangjc-select [options]="options" [multiple]="true" [formControl]="fruitsControl">
      <ng-shangjc-select-trigger placeholder="Select multiple fruits"></ng-shangjc-select-trigger>
    </ng-shangjc-select>
    
    <p>Selected values: {{ fruitsControl.value | json }}</p>
  `
})
export class MultiFormExampleComponent {
  fruitsControl = new FormControl<string[]>(['apple', 'banana']);
  
  options: SelectOption[] = [
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'orange', label: 'Orange' },
    { value: 'grape', label: 'Grape' }
  ];
}
```

### Controlled Component with Error State

```typescript
import { Component, signal } from '@angular/core';
import { 
  SelectComponent,
  SelectTriggerComponent,
  SelectOption
} from './ui/shangjc/select';

@Component({
  selector: 'app-controlled-example',
  standalone: true,
  imports: [SelectComponent, SelectTriggerComponent],
  template: `
    <ng-shangjc-select 
      [options]="options" 
      [(value)]="selectedValue"
      [error]="errorMessage()"
      [disabled]="isDisabled()">
      <ng-shangjc-select-trigger placeholder="Select a fruit"></ng-shangjc-select-trigger>
    </ng-shangjc-select>
    
    <button (click)="toggleDisabled()">Toggle Disabled</button>
    <button (click)="showError()">Show Error</button>
  `
})
export class ControlledExampleComponent {
  selectedValue = signal<string | null>(null);
  errorMessage = signal('');
  isDisabled = signal(false);
  
  options: SelectOption[] = [
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'orange', label: 'Orange' }
  ];

  toggleDisabled() {
    this.isDisabled.set(!this.isDisabled());
  }

  showError() {
    this.errorMessage.set('Please select a valid option');
    setTimeout(() => this.errorMessage.set(''), 3000);
  }
}
```

## API Reference

### SelectComponent

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `options` | `SelectOption[]` | `[]` | Array of options to display |
| `value` | `string \| string[] \| null` | `null` | Current selected value(s) |
| `disabled` | `boolean` | `false` | Whether the select is disabled |
| `searchable` | `boolean` | `false` | Enable search functionality |
| `multiple` | `boolean` | `false` | Enable multiple selection mode |
| `searchClass` | `string` | `''` | Custom class for the search input |
| `searchPlaceholder` | `string` | `''` | Placeholder text for the search input |
| `optionBoxClass` | `string` | `''` | Custom class for the options container |
| `id` | `string` | `''` | ID for the select component |
| `errorClass` | `string` | `''` | Additional classes for the error message |
| `error` | `string` | `''` | Error message displayed below the select |

| Event | Type | Description |
|-------|------|-------------|
| `selectionChange` | `EventEmitter<SelectOption \| SelectOption[] \| null>` | Emitted when selection changes |

### SelectTriggerComponent

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `asChild` | `boolean` | `false` | Whether to render the trigger as a child component |
| `class` | `string` | `''` | Additional CSS classes to apply |
| `placeholder` | `string` | `''` | Placeholder text when no option is selected |
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Size variant of the trigger |

### SelectItemComponent

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `option` | `SelectOption \| null` | `null` | Select Option object |
| `label` | `string` | `''` | Label for the select item |
| `value` | `string` | `''` | Value for the select item |
| `disabled` | `boolean` | `false` | Disables the select item |
| `class` | `string` | `''` | Custom class for the select item |

### SelectOption Interface

```typescript
interface SelectOption {
  value: string;
  label: string;
  disabled?: boolean;
  component?: boolean;
}
```

## Accessibility

- **Keyboard Navigation**: Full support for Tab, Shift+Tab, Enter, Space, Arrow keys, and Escape
- **ARIA Attributes**: Proper ARIA labels, roles, and states including `role="combobox"`, `role="listbox"`, `role="option"`, `aria-expanded`, `aria-controls`, `aria-activedescendant`, `aria-selected`, and `aria-disabled`
- **Focus Management**: Visible focus indicators and proper focus handling with keyboard navigation
- **Screen Reader**: Compatible with screen readers and assistive technologies
- **Semantic Structure**: Uses proper HTML semantic elements and ARIA landmarks

### ARIA Features

- `role="combobox"` on the trigger element
- `role="listbox"` on the options container
- `role="option"` on each option item
- `aria-expanded` indicates dropdown state
- `aria-controls` references the options container
- `aria-activedescendant` references the highlighted option
- `aria-selected` indicates selection state
- `aria-disabled` indicates disabled items

## Browser Support

- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+

## Contributing

See the main project [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.

---

**Part of ng-shangjc component library** • [Documentation](https://juliancallejas.github.io/ng-shangjc-docs/)

<p align="center">
<a href="https://github.com/JulianCallejas">
  <img src="https://res.cloudinary.com/dphleqb5t/image/upload/v1740784502/github-jc-develop/JC-LOGO-Horizontal-170-50-thin-github_uu3b5n.svg" width="170" alt="{ jc - develop }"  /> 
</a>
</p>