# GroupRadioBox

A flexible group radio box component that allows users to select a single option from a predefined list. Features comprehensive state management, custom styling variants, and focus/blur event handling for seamless form integration.

## Installation

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

## Import

```typescript
import GroupRadioBox from "@ticatec/uniface-element/GroupRadioBox";
import { DisplayMode } from "@ticatec/uniface-element";
import type { OnChangeHandler } from "@ticatec/uniface-element";
```

## Basic Usage

```svelte
<script>
  import GroupRadioBox from "@ticatec/uniface-element/GroupRadioBox";
  
  const options = [
    { code: "sm", text: "Small" },
    { code: "md", text: "Medium" },
    { code: "lg", text: "Large" },
    { code: "xl", text: "Extra Large" }
  ];
  
  let selectedSize = "md";
  
  function handleChange(value) {
    console.log('Selected size:', value);
  }
</script>

<GroupRadioBox 
  {options} 
  bind:value={selectedSize}
  onchange={handleChange}
/>
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `any` | required | Selected value |
| `options` | `Array<any>` | required | Array of option objects |
| `keyField` | `string` | `"code"` | Field name for option keys/values |
| `textField` | `string` | `"text"` | Field name for option display text |
| `disabled` | `boolean` | `false` | Whether the component is disabled |
| `readonly` | `boolean` | `false` | Whether the component is read-only |
| `displayMode` | `DisplayMode` | `DisplayMode.Edit` | Display mode (Edit or View) |
| `variant` | `"" \| "plain" \| "outlined" \| "filled"` | `""` | Visual variant |
| `compact` | `boolean` | `false` | Whether to use compact spacing |
| `disabledOptions` | `Array<string>` | `[]` | Array of option keys to disable |
| `style` | `string` | `""` | Additional CSS styles |
| `item$style` | `string` | `""` | CSS styles for individual radio button items |
| `onchange` | `OnChangeHandler<any>` | `null` | Change event handler |
| `onfocus` | `(() => void) \| null` | `null` | Focus event handler |
| `onblur` | `(() => void) \| null` | `null` | Blur event handler |

## Methods

| Method | Description |
|--------|-------------|
| `setFocus()` | Programmatically focus the first enabled radio button |

## Examples

### Basic Selection

```svelte
<script>
  import GroupRadioBox from "@ticatec/uniface-element/GroupRadioBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const priorities = [
    { code: "low", text: "Low Priority" },
    { code: "medium", text: "Medium Priority" },
    { code: "high", text: "High Priority" },
    { code: "urgent", text: "Urgent" }
  ];
  
  let taskPriority = "medium";
  
  function handlePriorityChange(value) {
    console.log('Priority changed to:', value);
    
    // Handle priority-specific logic
    if (value === 'urgent') {
      console.log('Urgent task - notify team leads');
    }
  }
</script>

<div class="demo-section">
  <FormField label="Task Priority">
    <GroupRadioBox 
      options={priorities}
      bind:value={taskPriority}
      onchange={handlePriorityChange}
      onfocus={() => console.log('Priority field focused')}
      onblur={() => console.log('Priority field blurred')}
    />
  </FormField>
  
  <div class="result">
    <p><strong>Selected Priority:</strong> {taskPriority}</p>
    <p><strong>Display Text:</strong> {priorities.find(p => p.code === taskPriority)?.text ?? 'None'}</p>
  </div>
</div>

<style>
  .demo-section {
    margin: 20px;
    padding: 20px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
  
  .result {
    margin-top: 16px;
    padding: 12px;
    background: #f8fafc;
    border-radius: 6px;
  }
</style>
```

### Different Visual Variants

```svelte
<script>
  import GroupRadioBox from "@ticatec/uniface-element/GroupRadioBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const themes = [
    { code: "light", text: "Light Theme" },
    { code: "dark", text: "Dark Theme" },
    { code: "auto", text: "Auto (System)" }
  ];
  
  let selectedTheme = "auto";
</script>

<div class="variants-demo">
  <div class="variant-section">
    <h3>Default Variant</h3>
    <FormField label="Theme Preference">
      <GroupRadioBox options={themes} bind:value={selectedTheme} />
    </FormField>
  </div>
  
  <div class="variant-section">
    <h3>Outlined Variant</h3>
    <FormField label="Theme Preference">
      <GroupRadioBox 
        options={themes} 
        variant="outlined"
        bind:value={selectedTheme} 
      />
    </FormField>
  </div>
  
  <div class="variant-section">
    <h3>Filled Variant</h3>
    <FormField label="Theme Preference">
      <GroupRadioBox 
        options={themes} 
        variant="filled"
        bind:value={selectedTheme} 
      />
    </FormField>
  </div>
  
  <div class="variant-section">
    <h3>Compact Variant</h3>
    <FormField label="Theme Preference">
      <GroupRadioBox 
        options={themes} 
        compact={true}
        bind:value={selectedTheme} 
      />
    </FormField>
  </div>
</div>

<style>
  .variants-demo {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
    margin: 20px;
  }
  
  .variant-section {
    padding: 16px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
</style>
```

### Conditional Options and Dynamic Behavior

```svelte
<script>
  import GroupRadioBox from "@ticatec/uniface-element/GroupRadioBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const paymentMethods = [
    { code: "card", text: "Credit Card" },
    { code: "paypal", text: "PayPal" },
    { code: "bank", text: "Bank Transfer" },
    { code: "crypto", text: "Cryptocurrency" },
    { code: "cash", text: "Cash on Delivery" }
  ];
  
  let selectedPayment = "card";
  let disabledOptions = [];
  let orderTotal = 150.00;
  let region = "domestic";
  
  // Update available payment methods based on order conditions
  $: {
    disabledOptions = [];
    
    // Disable crypto for small orders
    if (orderTotal < 100) {
      disabledOptions.push("crypto");
    }
    
    // Disable cash on delivery for international orders
    if (region === "international") {
      disabledOptions.push("cash");
    }
    
    // Auto-select available option if current selection becomes disabled
    if (disabledOptions.includes(selectedPayment)) {
      const availableOptions = paymentMethods.filter(method => 
        !disabledOptions.includes(method.code)
      );
      if (availableOptions.length > 0) {
        selectedPayment = availableOptions[0].code;
      }
    }
  }
  
  function handlePaymentChange(value) {
    console.log('Payment method changed to:', value);
    
    // Handle payment-specific logic
    if (value === 'crypto') {
      console.log('Crypto payment selected - show wallet options');
    } else if (value === 'bank') {
      console.log('Bank transfer selected - show bank details');
    }
  }
</script>

<div class="conditional-demo">
  <div class="controls">
    <FormField label="Order Total">
      <input 
        type="number" 
        bind:value={orderTotal} 
        min="10" 
        step="10"
        class="number-input"
      />
    </FormField>
    
    <FormField label="Region">
      <select bind:value={region} class="select-input">
        <option value="domestic">Domestic</option>
        <option value="international">International</option>
      </select>
    </FormField>
  </div>
  
  <FormField label="Payment Method">
    <GroupRadioBox 
      options={paymentMethods}
      bind:value={selectedPayment}
      {disabledOptions}
      onchange={handlePaymentChange}
      variant="outlined"
    />
  </FormField>
  
  <div class="info-panel">
    <p><strong>Selected:</strong> {selectedPayment}</p>
    <p><strong>Order Total:</strong> ${orderTotal}</p>
    <p><strong>Region:</strong> {region}</p>
    <p><strong>Disabled Methods:</strong> {disabledOptions.join(', ') || 'None'}</p>
    
    {#if disabledOptions.includes('crypto')}
      <p class="info-note">💡 Cryptocurrency payments require minimum $100 order</p>
    {/if}
    
    {#if disabledOptions.includes('cash')}
      <p class="info-note">💡 Cash on delivery not available for international orders</p>
    {/if}
  </div>
</div>

<style>
  .conditional-demo {
    margin: 20px;
    padding: 20px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
  
  .controls {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin-bottom: 20px;
  }
  
  .number-input, .select-input {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid #d1d5db;
    border-radius: 6px;
  }
  
  .info-panel {
    margin-top: 16px;
    padding: 12px;
    background: #f8fafc;
    border-radius: 6px;
  }
  
  .info-note {
    color: #6b7280;
    font-style: italic;
    margin-top: 8px;
  }
</style>
```

### Custom Field Configuration

```svelte
<script>
  import GroupRadioBox from "@ticatec/uniface-element/GroupRadioBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  // Using custom field names
  const departments = [
    { id: "eng", name: "Engineering", active: true },
    { id: "sales", name: "Sales & Marketing", active: true },
    { id: "hr", name: "Human Resources", active: false },
    { id: "finance", name: "Finance & Accounting", active: true }
  ];
  
  let selectedDepartment = "eng";
  
  function handleDepartmentChange(value) {
    const selected = departments.find(dept => dept.id === value);
    console.log('Department changed:', selected);
  }
</script>

<div class="custom-demo">
  <FormField label="Select Department">
    <GroupRadioBox 
      options={departments}
      keyField="id"
      textField="name"
      bind:value={selectedDepartment}
      onchange={handleDepartmentChange}
      variant="filled"
      style="padding: 16px; background: linear-gradient(145deg, #f8fafc, #e2e8f0);"
      item$style="margin: 8px 0; padding: 6px 12px; border-radius: 8px; background: white;"
    />
  </FormField>
  
  <div class="department-info">
    {#each departments as dept}
      {#if dept.id === selectedDepartment}
        <div class="selected-dept">
          <h4>{dept.name}</h4>
          <p>Status: {dept.active ? '✅ Active' : '❌ Inactive'}</p>
          <p>Department ID: {dept.id}</p>
        </div>
      {/if}
    {/each}
  </div>
</div>

<style>
  .custom-demo {
    margin: 20px;
    padding: 20px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
  
  .department-info {
    margin-top: 16px;
  }
  
  .selected-dept {
    padding: 16px;
    background: #dbeafe;
    border-radius: 8px;
    border-left: 4px solid #3b82f6;
  }
</style>
```

### Read-Only and Display Modes

```svelte
<script>
  import GroupRadioBox from "@ticatec/uniface-element/GroupRadioBox";
  import FormField from "@ticatec/uniface-element/FormField";
  import { DisplayMode } from "@ticatec/uniface-element";
  
  const subscriptionPlans = [
    { code: "basic", text: "Basic Plan" },
    { code: "premium", text: "Premium Plan" },
    { code: "enterprise", text: "Enterprise Plan" }
  ];
  
  let currentPlan = "premium";  // Pre-selected plan
</script>

<div class="readonly-demo">
  <div class="mode-section">
    <h3>Editable Mode</h3>
    <FormField label="Change Subscription Plan">
      <GroupRadioBox 
        options={subscriptionPlans}
        bind:value={currentPlan}
      />
    </FormField>
  </div>
  
  <div class="mode-section">
    <h3>Read-Only Mode</h3>
    <FormField label="Current Plan (Read-Only)">
      <GroupRadioBox 
        options={subscriptionPlans}
        value={currentPlan}
        readonly={true}
      />
    </FormField>
  </div>
  
  <div class="mode-section">
    <h3>Disabled Mode</h3>
    <FormField label="Plan Selection (Disabled)">
      <GroupRadioBox 
        options={subscriptionPlans}
        value={currentPlan}
        disabled={true}
      />
    </FormField>
  </div>
  
  <div class="mode-section">
    <h3>Display-Only Mode</h3>
    <FormField label="Active Plan">
      <GroupRadioBox 
        options={subscriptionPlans}
        value={currentPlan}
        displayMode={DisplayMode.View}
      />
    </FormField>
  </div>
</div>

<style>
  .readonly-demo {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
    margin: 20px;
  }
  
  .mode-section {
    padding: 16px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
</style>
```

### Form Integration with Validation

```svelte
<script>
  import GroupRadioBox from "@ticatec/uniface-element/GroupRadioBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const shippingOptions = [
    { code: "standard", text: "Standard (5-7 days)" },
    { code: "express", text: "Express (2-3 days)" },
    { code: "overnight", text: "Overnight" }
  ];
  
  const countries = [
    { code: "us", text: "United States" },
    { code: "ca", text: "Canada" },
    { code: "uk", text: "United Kingdom" },
    { code: "au", text: "Australia" }
  ];
  
  let formData = {
    name: '',
    country: '',
    shipping: ''
  };
  
  let errors = {};
  let groupRadioBoxRef;
  
  function validateForm() {
    errors = {};
    
    if (!formData.name.trim()) {
      errors.name = 'Name is required';
    }
    
    if (!formData.country) {
      errors.country = 'Please select a country';
    }
    
    if (!formData.shipping) {
      errors.shipping = 'Please select a shipping option';
    }
    
    return Object.keys(errors).length === 0;
  }
  
  function handleSubmit() {
    if (validateForm()) {
      console.log('Form submitted:', formData);
      alert('Order placed successfully!');
    } else {
      console.log('Validation errors:', errors);
      // Focus first error field
      if (errors.shipping) {
        groupRadioBoxRef.setFocus();
      }
    }
  }
  
  function handleShippingChange(value) {
    formData.shipping = value;
    // Clear error when user makes a selection
    if (errors.shipping && value) {
      delete errors.shipping;
      errors = { ...errors };
    }
  }
  
  function handleCountryChange(value) {
    formData.country = value;
    // Clear shipping selection if changing country (different options might be available)
    if (formData.shipping) {
      formData.shipping = '';
    }
    // Clear error
    if (errors.country && value) {
      delete errors.country;
      errors = { ...errors };
    }
  }
</script>

<div class="form-demo">
  <form on:submit|preventDefault={handleSubmit}>
    <FormField label="Full Name" error={errors.name}>
      <input 
        type="text" 
        bind:value={formData.name}
        class="form-input"
        class:error={errors.name}
        placeholder="Enter your full name"
      />
    </FormField>
    
    <FormField label="Country" error={errors.country}>
      <GroupRadioBox 
        options={countries}
        bind:value={formData.country}
        onchange={handleCountryChange}
        variant="outlined"
      />
    </FormField>
    
    <FormField label="Shipping Method" error={errors.shipping}>
      <GroupRadioBox 
        bind:this={groupRadioBoxRef}
        options={shippingOptions}
        bind:value={formData.shipping}
        onchange={handleShippingChange}
        variant="outlined"
      />
      <p class="field-hint">Shipping costs will be calculated based on your selection</p>
    </FormField>
    
    <div class="form-actions">
      <button type="submit" class="submit-btn">Place Order</button>
      <button type="button" on:click={() => console.log(formData)} class="preview-btn">
        Preview Order
      </button>
    </div>
    
    {#if formData.country && formData.shipping}
      <div class="order-summary">
        <h4>Order Summary</h4>
        <p>Ship to: {countries.find(c => c.code === formData.country)?.text}</p>
        <p>Shipping: {shippingOptions.find(s => s.code === formData.shipping)?.text}</p>
      </div>
    {/if}
  </form>
</div>

<style>
  .form-demo {
    max-width: 500px;
    margin: 20px;
    padding: 24px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
  
  .form-input {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid #d1d5db;
    border-radius: 6px;
  }
  
  .form-input.error {
    border-color: #dc2626;
  }
  
  .field-hint {
    margin-top: 4px;
    font-size: 0.875rem;
    color: #6b7280;
  }
  
  .form-actions {
    display: flex;
    gap: 12px;
    margin-top: 20px;
  }
  
  .submit-btn, .preview-btn {
    padding: 10px 20px;
    border-radius: 6px;
    border: none;
    cursor: pointer;
  }
  
  .submit-btn {
    background: #3b82f6;
    color: white;
  }
  
  .preview-btn {
    background: #f3f4f6;
    color: #374151;
    border: 1px solid #d1d5db;
  }
  
  .order-summary {
    margin-top: 20px;
    padding: 16px;
    background: #f0f9ff;
    border-radius: 8px;
    border-left: 4px solid #3b82f6;
  }
</style>
```

### Survey and Rating Implementation

```svelte
<script>
  import GroupRadioBox from "@ticatec/uniface-element/GroupRadioBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const satisfactionLevels = [
    { code: "1", text: "Very Dissatisfied" },
    { code: "2", text: "Dissatisfied" },
    { code: "3", text: "Neutral" },
    { code: "4", text: "Satisfied" },
    { code: "5", text: "Very Satisfied" }
  ];
  
  const frequencies = [
    { code: "daily", text: "Daily" },
    { code: "weekly", text: "Weekly" },
    { code: "monthly", text: "Monthly" },
    { code: "rarely", text: "Rarely" },
    { code: "never", text: "Never" }
  ];
  
  let surveyData = {
    satisfaction: '',
    frequency: '',
    recommendation: ''
  };
  
  let isCompleted = false;
  
  $: isCompleted = surveyData.satisfaction && surveyData.frequency && surveyData.recommendation;
  
  function handleSurveySubmit() {
    if (isCompleted) {
      console.log('Survey submitted:', surveyData);
      alert('Thank you for your feedback!');
    }
  }
  
  function getSatisfactionColor(level) {
    const colors = {
      '1': '#dc2626', '2': '#ea580c', '3': '#d97706',
      '4': '#65a30d', '5': '#16a34a'
    };
    return colors[level] || '#6b7280';
  }
</script>

<div class="survey-demo">
  <h2>Customer Satisfaction Survey</h2>
  
  <div class="survey-section">
    <FormField label="How satisfied are you with our service?">
      <GroupRadioBox 
        options={satisfactionLevels}
        bind:value={surveyData.satisfaction}
        variant="outlined"
        item$style="padding: 12px; margin: 4px 0; border-radius: 8px;"
      />
    </FormField>
    
    {#if surveyData.satisfaction}
      <div class="satisfaction-feedback" style="color: {getSatisfactionColor(surveyData.satisfaction)}">
        <strong>You selected: {satisfactionLevels.find(s => s.code === surveyData.satisfaction)?.text}</strong>
      </div>
    {/if}
  </div>
  
  <div class="survey-section">
    <FormField label="How often do you use our service?">
      <GroupRadioBox 
        options={frequencies}
        bind:value={surveyData.frequency}
        variant="filled"
        compact={true}
      />
    </FormField>
  </div>
  
  <div class="survey-section">
    <FormField label="Would you recommend us to others?">
      <GroupRadioBox 
        options={[
          { code: "yes", text: "Yes, definitely" },
          { code: "maybe", text: "Maybe" },
          { code: "no", text: "No, I wouldn't" }
        ]}
        bind:value={surveyData.recommendation}
        variant="outlined"
      />
    </FormField>
  </div>
  
  <div class="survey-progress">
    <div class="progress-bar">
      <div 
        class="progress-fill" 
        style="width: {(Object.values(surveyData).filter(v => v).length / 3) * 100}%"
      ></div>
    </div>
    <p>Progress: {Object.values(surveyData).filter(v => v).length}/3 questions completed</p>
  </div>
  
  <button 
    on:click={handleSurveySubmit}
    disabled={!isCompleted}
    class="submit-survey-btn"
    class:disabled={!isCompleted}
  >
    Submit Survey
  </button>
</div>

<style>
  .survey-demo {
    max-width: 600px;
    margin: 20px;
    padding: 24px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
  
  .survey-section {
    margin-bottom: 24px;
  }
  
  .satisfaction-feedback {
    margin-top: 8px;
    padding: 8px 12px;
    background: #f0f9ff;
    border-radius: 6px;
    font-weight: 500;
  }
  
  .survey-progress {
    margin: 24px 0;
  }
  
  .progress-bar {
    height: 8px;
    background: #e5e7eb;
    border-radius: 4px;
    overflow: hidden;
    margin-bottom: 8px;
  }
  
  .progress-fill {
    height: 100%;
    background: #3b82f6;
    transition: width 0.3s ease;
  }
  
  .submit-survey-btn {
    width: 100%;
    padding: 12px 24px;
    background: #3b82f6;
    color: white;
    border: none;
    border-radius: 8px;
    font-size: 16px;
    cursor: pointer;
    transition: all 0.2s;
  }
  
  .submit-survey-btn:hover:not(.disabled) {
    background: #2563eb;
  }
  
  .submit-survey-btn.disabled {
    background: #d1d5db;
    color: #9ca3af;
    cursor: not-allowed;
  }
</style>
```

## Styling

The GroupRadioBox component can be styled using CSS custom properties:

```css
.uniface-group-box {
  --radio-spacing: 8px;
  --radio-padding: 4px 8px;
  --border-color: #e2e8f0;
  --background-color: #ffffff;
  --hover-background: #f9fafb;
  --selected-color: #3b82f6;
}

/* Variant-specific styling */
.uniface-group-box.outlined {
  border: 1px solid var(--border-color);
  border-radius: 6px;
  padding: 12px;
}

.uniface-group-box.filled {
  background-color: #f8fafc;
  border-radius: 6px;
  padding: 12px;
}

.uniface-group-box.compact {
  --radio-spacing: 4px;
  --radio-padding: 2px 4px;
}
```

## Accessibility

The GroupRadioBox component includes several accessibility features:

- Keyboard navigation between radio buttons
- Focus management with `setFocus()` method
- ARIA labels and radio group semantics
- Screen reader announcements for selection changes
- Proper tab order management

## Best Practices

1. **Option Management**: Keep option arrays stable to prevent unnecessary re-renders
2. **Default Selection**: Always provide a sensible default value for better UX
3. **Validation**: Implement proper validation for required selections
4. **User Feedback**: Provide clear visual feedback for selection changes
5. **Accessibility**: Always provide meaningful labels and group descriptions
6. **State Management**: Handle focus/blur events appropriately for form validation

## License

MIT