# Time Span

Use `ui-time-span` when users need a reusable duration rather than a calendar date or a single time of day. It fits retention policies, token expiration, SLA settings, reminder delays, and any workflow where the stored value is a duration such as `PT1H30M`.

## Import
```ts
import { TimeSpanComponent, type TimeSpanValue } from 'ui';
```

## Basic duration selection
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonComponent, TimeSpanComponent } from 'ui';

@Component({
  selector: 'app-time-span-basic-demo',
  standalone: true,
  imports: [FormsModule, ButtonComponent, TimeSpanComponent],
  template: `
    <div style="display:flex;flex-direction:column;gap:1rem;max-width:24rem">
      <ui-time-span
        label="Reminder delay"
        placeholder="1h 30m"
        [showHours]="true"
        [showMinutes]="true"
        [(ngModel)]="value"
        [ngModelOptions]="{ standalone: true }"
      />

      <div
        style="display:flex;flex-wrap:wrap;gap:0.75rem;align-items:center;padding:0.75rem 0.875rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:0.875rem;background:var(--color-neutral-background-rest)"
      >
        <ui-button type="button" appearance="subtle" (click)="value = ''">Clear</ui-button>
        <span style="font-size:0.75rem;color:var(--color-neutral-foreground2-rest)">
          {{ value || 'No duration selected.' }}
        </span>
      </div>
    </div>
  `,
})
export class TimeSpanBasicDemoComponent {
  protected value = 'PT1H30M';
}
```

## Unit configurations
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TimeSpanComponent } from 'ui';

@Component({
  selector: 'app-time-span-unit-presets-demo',
  standalone: true,
  imports: [FormsModule, TimeSpanComponent],
  template: `
    <div
      style="display:grid;grid-template-columns:repeat(auto-fit,minmax(17rem,1fr));gap:1rem;align-items:start"
    >
      <ui-time-span
        label="Hours and minutes"
        placeholder="1h 30m"
        [showHours]="true"
        [showMinutes]="true"
        [(ngModel)]="hoursMinutes"
        [ngModelOptions]="{ standalone: true }"
      />

      <ui-time-span
        label="Days, hours, and minutes"
        placeholder="2d 4h"
        [showDays]="true"
        [showHours]="true"
        [showMinutes]="true"
        [(ngModel)]="daysHoursMinutes"
        [ngModelOptions]="{ standalone: true }"
      />

      <ui-time-span
        label="Years and months"
        placeholder="1y 3mo"
        [showYears]="true"
        [showMonths]="true"
        [showDays]="false"
        [showHours]="false"
        [showMinutes]="false"
        [showSeconds]="false"
        [(ngModel)]="yearsMonths"
        [ngModelOptions]="{ standalone: true }"
      />
    </div>
  `,
})
export class TimeSpanUnitPresetsDemoComponent {
  protected hoursMinutes = 'PT2H15M';
  protected daysHoursMinutes = 'P1DT4H30M';
  protected yearsMonths = 'P1Y6M';
}
```

## Size and input variant
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TimeSpanComponent } from 'ui';

@Component({
  selector: 'app-time-span-size-variant-demo',
  standalone: true,
  imports: [FormsModule, TimeSpanComponent],
  template: `
    <div
      style="display:grid;grid-template-columns:repeat(auto-fit,minmax(17rem,1fr));gap:1rem;align-items:start"
    >
      <ui-time-span
        label="Small filled"
        placeholder="45m"
        size="small"
        inputVariant="filled"
        [showHours]="true"
        [showMinutes]="true"
        [(ngModel)]="smallValue"
        [ngModelOptions]="{ standalone: true }"
      />

      <ui-time-span
        label="Medium filled gray"
        placeholder="2h"
        size="medium"
        inputVariant="filled-gray"
        [showHours]="true"
        [showMinutes]="true"
        [(ngModel)]="mediumValue"
        [ngModelOptions]="{ standalone: true }"
      />

      <ui-time-span
        label="Large underlined"
        placeholder="3d"
        size="large"
        inputVariant="underlined"
        [showDays]="true"
        [showHours]="true"
        [showMinutes]="true"
        [(ngModel)]="largeValue"
        [ngModelOptions]="{ standalone: true }"
      />
    </div>
  `,
})
export class TimeSpanSizeVariantDemoComponent {
  protected smallValue = 'PT45M';
  protected mediumValue = 'PT2H';
  protected largeValue = 'P3DT2H';
}
```

## Readonly, disabled, required, and clearable states
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TimeSpanComponent } from 'ui';

@Component({
  selector: 'app-time-span-states-demo',
  standalone: true,
  imports: [FormsModule, TimeSpanComponent],
  template: `
    <div
      style="display:grid;grid-template-columns:repeat(auto-fit,minmax(17rem,1fr));gap:1rem;align-items:start"
    >
      <ui-time-span
        label="Readonly"
        helpText="The duration stays visible but cannot be changed."
        [readonly]="true"
        [showHours]="true"
        [showMinutes]="true"
        [(ngModel)]="readonlyValue"
        [ngModelOptions]="{ standalone: true }"
      />

      <ui-time-span
        label="Disabled"
        helpText="The field is unavailable."
        [disabled]="true"
        [showDays]="true"
        [showHours]="true"
        [(ngModel)]="disabledValue"
        [ngModelOptions]="{ standalone: true }"
      />

      <ui-time-span
        label="Clearable required"
        helpText="Choose the timeout duration."
        [required]="true"
        [clearable]="true"
        [showMinutes]="true"
        [showSeconds]="true"
        [(ngModel)]="requiredValue"
        [ngModelOptions]="{ standalone: true }"
      />
    </div>
  `,
})
export class TimeSpanStatesDemoComponent {
  protected readonlyValue = 'PT8H';
  protected disabledValue = 'P2DT6H';
  protected requiredValue = '';
}
```

## Reactive form integration
```ts
import { Component, computed } from '@angular/core';
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { ButtonComponent, MessageBarComponent, TimeSpanComponent } from 'ui';

@Component({
  selector: 'app-time-span-reactive-form-demo',
  standalone: true,
  imports: [ReactiveFormsModule, ButtonComponent, MessageBarComponent, TimeSpanComponent],
  template: `
    <div
      style="display:flex;flex-direction:column;gap:1rem;max-width:24rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
    >
      <div style="display:flex;flex-direction:column;gap:0.25rem">
        <div style="font-size:0.9375rem;font-weight:600">Retention policy</div>
        <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
          Time span often matters as an ISO 8601 duration in real settings or policy forms.
        </div>
      </div>

      <ui-time-span
        label="Auto-archive after"
        helpText="The stored output is an ISO 8601 duration string."
        [required]="true"
        [showDays]="true"
        [showHours]="true"
        [showMinutes]="true"
        [formControl]="timeSpanControl"
      />

      @if (timeSpanControl.invalid && timeSpanControl.touched) {
        <ui-message-bar
          title="Duration required"
          message="Choose the retention duration before continuing."
          variant="warning"
          appearance="subtle"
          [dismissible]="false"
        />
      }

      <div
        style="display:flex;flex-wrap:wrap;gap:0.75rem;align-items:center;padding:0.75rem 0.875rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:0.875rem;background:var(--color-neutral-background-rest)"
      >
        <ui-button type="button" variant="primary" [disabled]="timeSpanControl.invalid">
          Save policy
        </ui-button>
        <ui-button type="button" appearance="subtle" (click)="reset()">Reset</ui-button>
        <span style="font-size:0.75rem;color:var(--color-neutral-foreground2-rest)">
          {{ summary() }}
        </span>
      </div>
    </div>
  `,
})
export class TimeSpanReactiveFormDemoComponent {
  protected readonly timeSpanControl = new FormControl<string | null>('P7DT12H', {
    nonNullable: false,
    validators: [Validators.required],
  });

  protected readonly summary = computed(
    () => this.timeSpanControl.value || 'No duration selected.',
  );

  protected reset(): void {
    this.timeSpanControl.reset('P7DT12H');
  }
}
```

## Policy panel composition
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonComponent, MessageBarComponent, TagComponent, TimeSpanComponent } from 'ui';

@Component({
  selector: 'app-time-span-policy-panel-demo',
  standalone: true,
  imports: [FormsModule, ButtonComponent, MessageBarComponent, TagComponent, TimeSpanComponent],
  template: `
    <div
      style="display:grid;grid-template-columns:repeat(auto-fit,minmax(18rem,1fr));gap:1rem;align-items:start;max-width:48rem"
    >
      <div
        style="display:flex;flex-direction:column;gap:1rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <div style="display:flex;flex-direction:column;gap:0.25rem">
          <div style="font-size:0.9375rem;font-weight:600">Session policy</div>
          <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
            Time span is strongest when users need a reusable duration rather than one calendar
            date.
          </div>
        </div>

        <ui-time-span
          label="Session timeout"
          helpText="Use hours and minutes for the active timeout policy."
          [showHours]="true"
          [showMinutes]="true"
          [clearable]="true"
          [(ngModel)]="value"
          [ngModelOptions]="{ standalone: true }"
        />

        <ui-message-bar
          title="Policy note"
          message="The selected duration is stored as an ISO 8601 value and reused by the backend policy engine."
          variant="info"
          appearance="subtle"
          [dismissible]="false"
        />
      </div>

      <div
        style="display:flex;flex-direction:column;gap:0.875rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <div style="display:flex;flex-wrap:wrap;gap:0.5rem">
          <ui-tag text="Security" appearance="filled" variant="secondary" />
          <ui-tag text="ISO 8601 output" appearance="subtle" variant="info" />
          <ui-tag text="Ready to apply" appearance="subtle" variant="success" />
        </div>

        <div style="display:flex;flex-direction:column;gap:0.375rem">
          <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
            Selected duration
          </div>
          <div style="font-size:0.9375rem;font-weight:600">{{ value || 'Not selected' }}</div>
        </div>

        <div
          style="display:flex;flex-wrap:wrap;gap:0.75rem;align-items:center;padding:0.75rem 0.875rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:0.875rem;background:var(--color-neutral-background-rest)"
        >
          <ui-button type="button" variant="primary">Apply policy</ui-button>
          <ui-button type="button" appearance="subtle" (click)="value = ''">Reset</ui-button>
        </div>
      </div>
    </div>
  `,
})
export class TimeSpanPolicyPanelDemoComponent {
  protected value = 'PT8H30M';
}
```

## Accessibility

### Input and popup behavior
`ui-time-span` behaves like a text field and opens a dialog-style panel with wheel-like unit selectors for the enabled duration units.

| Element | Behavior |
| --- | --- |
| trigger input | exposes `aria-haspopup="dialog"` and `aria-expanded` |
| disabled or readonly | prevents opening or changing the duration |
| clear action | removes the current duration when `clearable` is enabled |

### Keyboard
Keyboard behavior follows the field and wheel-selector model.

| Key | Action |
| --- | --- |
| `Tab` / `Shift+Tab` | moves through the field and unit wheels |
| `Enter` / click on trigger actions | opens the duration picker panel |
| `ArrowUp` / `ArrowDown` on a wheel | decreases or increases the current unit |
| `PageUp` / `PageDown` on a wheel | changes the current unit in larger steps |

### Labels and guidance
Use a precise label such as session timeout, reminder delay, or retention duration so users understand what the duration affects. Helper text should mention the output format or the business meaning when the stored ISO 8601 string matters.
