# ProgressIndicator (mobile)

## Purpose

`ProgressIndicator` communicates **determinate** progress the user is advancing
through — multi-step flows, uploads with a known size, and similar. It is the
React Native counterpart to the web `@jobber/components` `ProgressIndicator` and
the replacement for the mobile `ProgressBar`.

For **indeterminate** activity the user cannot control (loading, fetching), use
an activity indicator instead — not `ProgressIndicator`.

## API

| Prop                 | Type                             | Default        | Description                                                 |
| -------------------- | -------------------------------- | -------------- | ----------------------------------------------------------- |
| `value`              | `number` (required)              | —              | Current progress, in the range `0..max`.                    |
| `max`                | `number`                         | `100`          | Maximum value.                                              |
| `pendingValue`       | `number`                         | `0`            | Additive in-flight progress beyond `value` (mobile-only).   |
| `variation`          | `"continuous" \| "stepped"`      | `"continuous"` | Single fill vs. one segment per integer step from `1..max`. |
| `size`               | `"smaller" \| "small" \| "base"` | `"base"`       | Bar height: 4 / 8 / 16px.                                   |
| `accessibilityLabel` | `string`                         | derived        | Accessible label override (see Accessibility).              |
| `style`              | `StyleProp<ViewStyle>`           | —              | Applied to the root element.                                |

## Accessibility

The root renders `<View accessibilityRole="progressbar">` with
`accessibilityValue={{ now: value, min: 0, max }}` and the resolved
`accessibilityLabel`. The component is not focusable.

When `accessibilityLabel` is omitted, it is derived via `useAtlantisI18n`:

- `"{value}%"` when `max === 100` (or omitted) and `pendingValue` is `0`;
- `"{value} / {max}"` when `max !== 100` and `pendingValue` is `0`;
- `"{value} of {max}, {pendingValue} pending"` when `pendingValue > 0`.

In the stepped variation the segments are presentational only — they carry no
role or accessibility value. The whole indicator announces once as a single
progressbar.

## Animation Model

The continuous fill (and the pending band, when present) animate their width
with `react-native-reanimated` `withTiming` over the `timing-base` token. The
stepped variation is not animated.

The width transition is **preserved** under a reduce-motion preference. The
slide between progress values conveys functional progress and is exempt under
WCAG 2.3.3 ("Animation from Interactions"); removing it would make progress
harder to perceive, not easier. This is the opposite of an indeterminate
activity indicator, whose motion IS suppressed under reduced motion.

### Pending overlay

`pendingValue` is **additive**: with `value={2}`, `pendingValue={3}`,
`max={10}`, the bar shows 2/10 completed and an additional 3/10 in flight, for
5/10 visually filled. In the continuous variation the pending band spans
`value .. value + pendingValue`; in the stepped variation the segments at
`value < pos <= value + pendingValue` are the pending state.

## Tokens

- Fill / filled segments: `color-interactive--subtle`.
- Track / empty segments: `color-surface--active`.
- Pending band / pending segments: `color-informative`.

The fill and track tokens mirror the web `ProgressIndicator`. The pending color
is carried forward from today's `ProgressBar` `inProgress` overlay so the
pending state keeps its established meaning; a dedicated pending tone is an open
question for design. No hardcoded colors and no drop shadow are used; all colors
resolve through `AtlantisThemeContext` and adapt across light and dark themes.

## Relationship to `ProgressBar`

`ProgressBar` is deprecated in favor of `ProgressIndicator` but continues to
work unchanged. Migration mapping:

| `ProgressBar`            | `ProgressIndicator`                                         |
| ------------------------ | ----------------------------------------------------------- |
| `current`                | `value`                                                     |
| `total`                  | `max`                                                       |
| `inProgress`             | `pendingValue` (same additive meaning)                      |
| `variation="progress"`   | `variation="continuous"`                                    |
| `loading`                | render `value={0}` or conditionally mount the component     |
| `header`                 | wrap the indicator in your own `View` / `Content` / `Stack` |
| `reverseTheme`           | wrap in `ThemeContextOverride` for the dark surface         |
| `UNSAFE_style.container` | `style`                                                     |
| other `UNSAFE_style.*`   | no direct replacement — compose around the component        |

## Cross-Platform Note

The `value` / `max` prop surface is aligned with the web `ProgressIndicator`.
`pendingValue` is mobile-only — web deferred it to this component.

## Ownership

Owned by the Atlantis / UX Foundations team. Ticket: JOB-167172.

## Out of Scope

- A `variant="ring"` shape (deferred to v2, pending design).
- Shimming `ProgressBar` onto `ProgressIndicator` (or removing it).
- A mobile `ActivityIndicator` Atlantis wrapper.
- Migrating jobber-mobile consumers off `ProgressBar`.
