# SpinBox

A bounded number with steppers, clamped on the way in.

```slint
import { SpinBox } from "@glint/components/spin-box.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 300px;
    background: Tokens.color-background;

    in-out property <float> item-quantity: 3;

    VerticalLayout {
        alignment: center;
        spacing: 16px;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            SpinBox {
                minimum: 1;
                maximum: 20;
                value <=> root.item-quantity;
            }
        }

        Text {
            text: "Quantity selected: " + Math.round(root.item-quantity);
            color: Tokens.color-muted-foreground;
            font-size: Tokens.typography-body-sm-size;
            horizontal-alignment: center;
        }
    }
}
```

## Usage

```slint
import { SpinBox } from "@glint/components/spin-box.slint";

export component AppWindow inherits Window {
    in-out property <float> guest-count: 2;

    VerticalLayout {
        alignment: center;

        SpinBox {
            minimum: 1;
            maximum: 10;
            step: 1;
            value <=> root.guest-count;
            changed(new-count) => {
                // handle quantity update
            }
        }
    }
}
```

`SpinBox` allows users to increase or decrease numeric values using stepper chevron buttons, keyboard arrow keys, or direct numerical typing with automatic bounds clamping upon commit.

## Examples

### Custom step increments

Use `step` to define the amount added or subtracted per step (such as `step: 5` or `step: 0.5`).

```slint
import { SpinBox } from "@glint/components/spin-box.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 300px;
    background: Tokens.color-background;

    in-out property <float> percentage: 50;

    VerticalLayout {
        alignment: center;
        spacing: 16px;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            SpinBox {
                minimum: 0;
                maximum: 100;
                step: 10;
                value <=> root.percentage;
            }
        }

        Text {
            text: "Stepped Value: " + Math.round(root.percentage) + "%";
            color: Tokens.color-muted-foreground;
            font-size: Tokens.typography-body-sm-size;
            horizontal-alignment: center;
        }
    }
}
```

### Strict bounds clamping

Typing an out-of-range number automatically clamps to `minimum` and `maximum` upon pressing `Enter` or blurring the field.

```slint
import { SpinBox } from "@glint/components/spin-box.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 300px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            SpinBox {
                minimum: 5;
                maximum: 15;
                value: 10;
            }
        }
    }
}
```

### Disabled state

Setting `enabled: false` dims the spinbox and disables typing and stepper buttons.

```slint
import { SpinBox } from "@glint/components/spin-box.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 300px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            SpinBox {
                enabled: false;
                value: 42;
            }
        }
    }
}
```

## API Reference

### Properties

| Property  | Type           | Default    | Description                                                                                                                               |
| --------- | -------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `value`   | `in-out float` | `0`        | Two-way; kept inside `minimum..maximum` (see the header note about an out-of-range initial binding).                                      |
| `minimum` | `in float`     | `0`        | Lower bound (inclusive).                                                                                                                  |
| `maximum` | `in float`     | `100`      | Upper bound (inclusive).                                                                                                                  |
| `step`    | `in float`     | `1`        | Amount one stepper click or one arrow key adds or removes.                                                                                |
| `enabled` | `in bool`      | `true`     | When false, the control dims and stops accepting input.                                                                                   |
| `text`    | `out string`   | no default | What the field shows. Equal to `value` except mid-edit, while a half-typed entry ("1", "-", or one that overshoots a bound) is on screen. |

### Callbacks

| Callback         | Description                                            |
| ---------------- | ------------------------------------------------------ |
| `changed(float)` | Fired with the new `value` whenever it actually moves. |

### Functions

| Function      | Description                               |
| ------------- | ----------------------------------------- |
| `increment()` | Adds one `step`, bounded by `maximum`.    |
| `decrement()` | Removes one `step`, bounded by `minimum`. |

## Accessibility

- **Spinbox role.** Publishes a single `accessible-role: spinbox` node with `accessible-value`, `accessible-value-minimum`, `accessible-value-maximum`, and `accessible-value-step`.
- **Keyboard navigation.** `↑` increments, `↓` decrements, and typed numbers are clamped upon `Enter`.
- **Focus ring.** Displays a focus ring when active.
