# NumberRange

A dual number input component for specifying numeric ranges, with automatic validation to ensure the "from" value doesn't exceed the "to" value.

## Features

- **Range Input**: Two connected number inputs for "from" and "to" values
- **Automatic Validation**: Ensures logical range order (from ≤ to)
- **Negative Numbers**: Optional support for negative values
- **Boundary Constraints**: Set min/max limits for the entire range
- **Multiple Variants**: Support for outlined, filled, plain, and default styles
- **Compact Mode**: Space-efficient layout for dense forms
- **Clear Functionality**: Built-in clear button to reset both values
- **Real-time Validation**: Immediate feedback during input
- **Focus Management**: Automatic focus handling between inputs

## Installation

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

## Basic Usage

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let fromValue = 10;
  let toValue = 50;
</script>

<NumberRange bind:fromValue bind:toValue />
```

## API Reference

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `fromValue` | `number \| null` | `null` | The "from" (minimum) value of the range |
| `toValue` | `number \| null` | `null` | The "to" (maximum) value of the range |
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | Visual style variant |
| `compact` | `boolean` | `false` | Enables compact display mode |
| `style` | `string` | `''` | Custom CSS styles |
| `min` | `number \| null` | `null` | Minimum allowed value for both inputs |
| `max` | `number \| null` | `null` | Maximum allowed value for both inputs |
| `allowNegative` | `boolean` | `false` | Allow negative numbers in both inputs |
| `class` | `string` | `''` | CSS class name |

## Examples

### Basic Range Selection

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  import FormField from '@ticatec/uniface-element/FormField';
  
  let ageFrom = 18;
  let ageTo = 65;
</script>

<FormField label="Age Range">
  <NumberRange 
    bind:fromValue={ageFrom} 
    bind:toValue={ageTo}
    variant="outlined"
    min={0}
    max={120}
  />
</FormField>
```

### Price Range Filter

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let priceMin = 100;
  let priceMax = 1000;
  
  $: console.log(`Price range: $${priceMin} - $${priceMax}`);
</script>

<div class="price-filter">
  <label>Price Range ($)</label>
  <NumberRange 
    bind:fromValue={priceMin} 
    bind:toValue={priceMax}
    variant="filled"
    min={0}
    max={10000}
  />
  <div class="range-display">
    ${priceMin || 0} - ${priceMax || 0}
  </div>
</div>
```

### Temperature Range (with negative values)

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let tempMin = -10;
  let tempMax = 25;
</script>

<div class="temperature-range">
  <label>Temperature Range (°C)</label>
  <NumberRange 
    bind:fromValue={tempMin} 
    bind:toValue={tempMax}
    variant="outlined"
    allowNegative={true}
    min={-50}
    max={50}
  />
</div>
```

### Search Criteria Form

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  import CriteriaField from '@ticatec/uniface-element/CriteriaField';
  
  let criteria = {
    salaryFrom: null,
    salaryTo: null,
    experienceFrom: 0,
    experienceTo: 10,
    ageFrom: 22,
    ageTo: 60
  };
  
  function handleSearch() {
    console.log('Search criteria:', criteria);
  }
</script>

<div class="search-form">
  <CriteriaField label="Salary Range" size="x30">
    <NumberRange 
      bind:fromValue={criteria.salaryFrom} 
      bind:toValue={criteria.salaryTo}
      variant="outlined"
      min={0}
      max={500000}
    />
  </CriteriaField>
  
  <CriteriaField label="Years of Experience" size="x30">
    <NumberRange 
      bind:fromValue={criteria.experienceFrom} 
      bind:toValue={criteria.experienceTo}
      variant="outlined"
      min={0}
      max={50}
    />
  </CriteriaField>
  
  <CriteriaField label="Age Range" size="x30">
    <NumberRange 
      bind:fromValue={criteria.ageFrom} 
      bind:toValue={criteria.ageTo}
      variant="outlined"
      min={16}
      max={80}
    />
  </CriteriaField>
  
  <button on:click={handleSearch}>Search</button>
</div>
```

### Compact Layout for Tables

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let filters = [
    { label: 'Score', from: 80, to: 100 },
    { label: 'Quantity', from: 1, to: 999 },
    { label: 'Rating', from: 3, to: 5 }
  ];
</script>

<table class="filter-table">
  <thead>
    <tr>
      <th>Filter</th>
      <th>Range</th>
    </tr>
  </thead>
  <tbody>
    {#each filters as filter}
      <tr>
        <td>{filter.label}</td>
        <td>
          <NumberRange 
            bind:fromValue={filter.from} 
            bind:toValue={filter.to}
            compact
            variant="outlined"
            min={0}
          />
        </td>
      </tr>
    {/each}
  </tbody>
</table>
```

### Dynamic Range with Validation

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let budgetMin = 1000;
  let budgetMax = 5000;
  let isValidRange = true;
  
  $: isValidRange = budgetMin <= budgetMax;
  $: rangeSize = budgetMax - budgetMin;
  
  function resetRange() {
    budgetMin = 1000;
    budgetMax = 5000;
  }
</script>

<div class="budget-selector">
  <label>Budget Range</label>
  <NumberRange 
    bind:fromValue={budgetMin} 
    bind:toValue={budgetMax}
    variant="outlined"
    min={0}
    max={100000}
  />
  
  <div class="range-info" class:error={!isValidRange}>
    {#if isValidRange}
      <span>Range size: ${rangeSize}</span>
    {:else}
      <span>Invalid range: minimum cannot exceed maximum</span>
    {/if}
  </div>
  
  <button on:click={resetRange}>Reset</button>
</div>

<style>
  .range-info.error {
    color: #d32f2f;
  }
</style>
```

### Statistical Analysis Range

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let dataRange = { min: -100, max: 100 };
  let outlierThreshold = { from: -50, to: 50 };
  
  $: normalDataPercentage = calculateNormalData(dataRange, outlierThreshold);
  
  function calculateNormalData(range, threshold) {
    const totalRange = range.max - range.min;
    const normalRange = threshold.to - threshold.from;
    return ((normalRange / totalRange) * 100).toFixed(1);
  }
</script>

<div class="analysis-panel">
  <h3>Data Analysis</h3>
  
  <div class="range-control">
    <label>Data Range</label>
    <NumberRange 
      bind:fromValue={dataRange.min} 
      bind:toValue={dataRange.max}
      variant="filled"
      allowNegative={true}
      min={-1000}
      max={1000}
    />
  </div>
  
  <div class="range-control">
    <label>Normal Range (exclude outliers)</label>
    <NumberRange 
      bind:fromValue={outlierThreshold.from} 
      bind:toValue={outlierThreshold.to}
      variant="outlined"
      allowNegative={true}
      min={dataRange.min}
      max={dataRange.max}
    />
  </div>
  
  <div class="statistics">
    <p>Normal data coverage: {normalDataPercentage}%</p>
  </div>
</div>
```

### Inventory Management

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let stockLevels = {
    lowStock: 0,
    highStock: 100,
    reorderFrom: 10,
    reorderTo: 90
  };
  
  $: isValidReorderRange = (
    stockLevels.reorderFrom >= stockLevels.lowStock &&
    stockLevels.reorderTo <= stockLevels.highStock &&
    stockLevels.reorderFrom <= stockLevels.reorderTo
  );
</script>

<div class="inventory-config">
  <h3>Inventory Configuration</h3>
  
  <div class="config-row">
    <label>Stock Level Range</label>
    <NumberRange 
      bind:fromValue={stockLevels.lowStock} 
      bind:toValue={stockLevels.highStock}
      variant="outlined"
      min={0}
      max={10000}
    />
  </div>
  
  <div class="config-row">
    <label>Reorder Range</label>
    <NumberRange 
      bind:fromValue={stockLevels.reorderFrom} 
      bind:toValue={stockLevels.reorderTo}
      variant="filled"
      min={stockLevels.lowStock}
      max={stockLevels.highStock}
    />
  </div>
  
  <div class="validation" class:valid={isValidReorderRange}>
    {isValidReorderRange ? '✓ Valid configuration' : '✗ Invalid reorder range'}
  </div>
</div>

<style>
  .validation.valid {
    color: #2e7d32;
  }
  .validation:not(.valid) {
    color: #d32f2f;
  }
</style>
```

## Validation Logic

The NumberRange component automatically enforces logical constraints:

1. **Range Order**: The "from" value cannot exceed the "to" value
2. **Boundary Respect**: Both values respect the global min/max constraints
3. **Cross-validation**: When one value changes, the other adjusts if necessary
4. **Real-time Feedback**: Validation occurs during typing

## Best Practices

1. **Set Appropriate Boundaries**: Use min/max to prevent unrealistic values
2. **Provide Clear Labels**: Indicate what the range represents
3. **Handle Empty States**: Consider what happens when values are null
4. **Use Compact Mode Wisely**: In dense layouts or tables
5. **Validate Range Logic**: Ensure business rules are met
6. **Consider User Workflow**: Which input should users typically fill first
7. **Provide Feedback**: Show range calculations or validation messages

## Styling

The NumberRange component uses the following CSS structure:

```css
.uniface-range-editor {
  display: flex;
  align-items: center;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
}

.uniface-range-editor .divider {
  width: 8px;
  height: 1px;
  background: #ccc;
  margin: 0 4px;
}

.uniface-range-editor.compact {
  padding: 4px 8px;
}

.uniface-range-editor.outlined {
  border: 2px solid #1976d2;
}

.uniface-range-editor.filled {
  background-color: #f5f5f5;
}
```

## Accessibility

- Proper ARIA labels for screen readers
- Keyboard navigation between inputs
- Focus management and visual indicators
- Support for high contrast modes
- Logical tab order

## Related Components

- [NumberEditor](../number-editor/README.md) - For single number input
- [DateRange](../date-range/README.md) - For date range selection
- [CriteriaField](../criteria-field/README.md) - For search form layouts
- [FormField](../form-field/README.md) - For form field labels and layout