# Spinner

Displays a compact loading indicator for inline status, buttons, badges, input groups, and empty states.

Use Spinner when work is in progress but the surrounding layout should stay visible and composable.

## Import

```ts
import { SpinnerComponent } from '@edsis/component/spinner';
```

Bring in companion entrypoints such as Button, Badge, Item, Empty, or Input Group only when the surrounding example needs them.

## Basic usage

By default, `Spinner` exposes `role="status"` with an accessible name of `Loading`.

```html
<Spinner aria-label="Loading invoices" />
```

## Common patterns

### Size and color

Use the `class` input to change size, color, or surrounding spacing.

```html
<div class="flex items-center gap-4">
  <Spinner class="size-3" aria-label="Refreshing alerts" />
  <Spinner class="size-4 text-primary" aria-label="Refreshing dashboard" />
  <Spinner class="size-6 text-muted-foreground" aria-label="Loading analytics" />
</div>
```

### Decorative inline status

When nearby text already explains the loading state, mark the spinner as decorative so assistive technology does not hear duplicate status announcements.

```html
<button Button type="button" disabled class="gap-2">
  <Spinner decorative />
  Loading...
</button>
```

### Button and badge composition

There is no React-style `data-icon` API in the Angular library. Place `Spinner` before or after the visible label and use standard gap utilities on the host surface.

```html
<button Button type="button" variant="outline" disabled class="gap-2">
  <Spinner decorative />
  Please wait
</button>

<Badge class="gap-1.5">
  <Spinner decorative class="size-3" />
  Syncing
</Badge>
```

### Item row

The upstream preview maps cleanly to the local Item primitive.

```html
<Item variant="muted" class="w-full max-w-xs [--radius:1rem]">
  <ItemMedia>
    <Spinner decorative />
  </ItemMedia>
  <ItemContent>
    <ItemTitle>Processing payment...</ItemTitle>
  </ItemContent>
  <ItemContent class="flex-none justify-end">
    <span class="text-sm tabular-nums">$100.00</span>
  </ItemContent>
</Item>
```

### Input group status

Spinner works as projected content inside `InputGroupAddon` for inline or block-end status rows.

```html
<InputGroup>
  <input InputGroupInput placeholder="Send a message..." disabled />
  <InputGroupAddon align="inline-end">
    <Spinner decorative />
  </InputGroupAddon>
</InputGroup>
```

### Empty state

Project the spinner into `EmptyMedia` when a request is still processing.

```html
<Empty class="w-full max-w-xl rounded-2xl border border-border bg-card/40">
  <EmptyHeader>
    <EmptyMedia variant="icon">
      <Spinner decorative class="size-5" />
    </EmptyMedia>
    <EmptyTitle>Processing your request</EmptyTitle>
    <EmptyDescription>Please wait while the operation completes.</EmptyDescription>
  </EmptyHeader>
</Empty>
```

### RTL

Set `dir="rtl"` on the surrounding container or layout shell. The spinner stays direction-agnostic while the surrounding row flips logically.

```html
<section dir="rtl" lang="ar" class="w-full max-w-xs">
  <Item variant="muted">
    <ItemMedia>
      <Spinner decorative />
    </ItemMedia>
    <ItemContent>
      <ItemTitle>جاري معالجة الدفع...</ItemTitle>
    </ItemContent>
  </Item>
</section>
```

## API reference

| Input             | Type             | Default     | Notes                                                   |
| ----------------- | ---------------- | ----------- | ------------------------------------------------------- |
| `class`           | `string`         | `''`        | Controls size, color, and layout spacing.               |
| `decorative`      | `boolean`        | `false`     | Removes status semantics and sets `aria-hidden="true"`. |
| `aria-label`      | `string`         | `'Loading'` | Accessible name when the spinner stands alone.          |
| `aria-labelledby` | `string \| null` | `null`      | Connects the spinner to nearby visible text.            |

Native host attributes such as `id`, `dir`, `data-*`, and custom utility classes still pass through to the rendered custom element.

## Styling and theming

The spinner inherits `currentColor`, so it automatically matches the surrounding button, badge, item, or muted text color unless a utility overrides it.

The default animation uses `animate-spin` and automatically stops under `prefers-reduced-motion`.

## Accessibility

- Default host is a named `status` element for standalone loading indicators.
- Set `decorative` when nearby text already announces the loading state.
- Use `aria-labelledby` instead of a second invisible label when visible copy already exists in the same row.
- Keep the spinner paired with meaningful text or layout context for passive loading states.

## Angular notes

- `Spinner` is a standalone component with no providers or services.
- Size and color remain utility-driven instead of introducing React-style variant props.
- The `decorative` input is the Angular-first way to avoid repeated status announcements inside composed controls.

## Source parity

This Angular implementation follows the shadcn Spinner page closely for preview, sizing, buttons, badges, input groups, empty states, and RTL composition while translating the API to standalone imports, utility-driven styling, and explicit accessibility bindings.
