# optimisticToggleSync

> Why JS: a form post replaces the page, so script is what flips a toggle the moment it is pressed, posts the same form data in the background, and puts the toggle back on failure.

```ts
import { optimisticToggleSync } from "@sdxc/ui/app";
```

Applied to the toggle's `<form>`: on submit it flips `data-pressed`, the button's
`aria-pressed`, tone and name, and the hidden next-state field, then posts the form as it stood.
A refused post flips it back, sets `data-failed` and writes the `refused` copy for its status,
or `failed`, into the live region.
The newest press wins over a slow answer to an older one, and only its answer is reported.

## Signature

```ts
optimisticToggleSync(options: OptimisticToggleSync.Options): MixinDescriptor<HTMLFormElement>
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| `options` | `OptimisticToggleSync.Options` |  |

## Examples

```tsx
<form method="post" action={url} mix={[optimisticToggleSync({ failed: "Failed" })]}>…</form>
```

## Used with it

The events, constants and types its module publishes, each imported from `@sdxc/ui/app` too.

### OptimisticToggleEvent

Dispatched, bubbling, from a toggle's form at the press and again once the server answers,
so a row can restyle at once and reload server-derived counts only when `settled` is true.

#### Signature

```ts
new OptimisticToggleEvent(pressed: boolean, outcome?: OptimisticToggleSync.Outcome)
```

#### Properties

| Property | Type | Description |
| --- | --- | --- |
| `pressed` | `readonly boolean` |  |
| `failed` | `readonly boolean` | Whether the server refused the last change, which put the toggle back. |
| `settled` | `readonly boolean` | Whether the server has answered the press: false for the optimistic flip, true after. |
| `status` | `readonly number \| null` | The answer's HTTP status, for picking copy; `null` before the answer and for a network error. |

### OPTIMISTIC_TOGGLE_HEADER

Header a background post carries, so the server can answer with a status instead of a redirect.

#### Signature

```ts
const OPTIMISTIC_TOGGLE_HEADER = "x-optimistic-toggle"
```

### toneOf

Picks the button's tone: danger after a refusal, brand while on, neutral while off.

#### Signature

```ts
toneOf(pressed: boolean, failed: boolean): "danger" | "brand" | "neutral"
```

Returns: The semantic color role the button wears.

#### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| `pressed` | `boolean` | Whether the toggle is on. |
| `failed` | `boolean` | Whether the last change was refused. |

### OptimisticToggleSync.Options

Configuration for optimisticToggleSync.

#### Members

| Member | Type | Description |
| --- | --- | --- |
| `failed` | `string` | What the form's live region says once the server refuses a change, such as "Failed". |
| `refused?` | `Record<number, string>` | What it says instead for a refusal with one of these statuses, such as a full shelf's 507. |
| `label?` | `string` | The button's title and, when it has one, `aria-label` while off; left as rendered when omitted. |
| `pressedLabel?` | `string` | The button's title and `aria-label` while on; `label` when omitted. |

### OptimisticToggleSync.Outcome

Where a press stands, as OptimisticToggleEvent reports it.

#### Members

| Member | Type | Description |
| --- | --- | --- |
| `failed?` | `boolean` | The server refused the change. |
| `settled?` | `boolean` | The server answered, with success or a refusal. |
| `status?` | `number \| null` | The answer's HTTP status; `null` before the answer and for a network error. |
