# Repeat Field

> How a session repeats, one question at a time: once, every week, or every 2 weeks, then the days, then until when. Under it, the series in words with its count, and any sessions that clash with a closure, with a way to skip them.

- Name: `repeat-field`
- Status: beta
- Categories: forms
- File: the install writes `repeat-field.tsx` to your `ui` alias, `@/components/ui` by default.
- Other registry items: `@arbokui/motion`, `@arbokui/button`, `@arbokui/choice-chips`, `@arbokui/date-picker`, `@arbokui/field-frame`, `@arbokui/selection-card`, `@arbokui/status-text`, `@arbokui/tool-button`. The install adds them.
- Gallery: https://arbokui.com/components/repeat-field
- Registry item: https://arbokui.com/r/repeat-field.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/repeat-field
   ```

## Use it for

- Setting up a class, a team meeting, or any session that repeats through a season.
- Pass start, the first session, and time, which the series says back, like "19:00 to 20:30".
- Pass seasonEnd to offer "End of season". Without it, the field asks for a last day.
- Pass closed with each closure and why, like a room being repainted. Sessions that fall in one say so, and Skip them leaves them out.
- Skip them goes on ⌘Z for 8 seconds, and a Toaster on the page shows it with Undo.
- Every 2 weeks counts from the start's own week.

## Avoid it for

- A single date. Use a date picker.
- Rules a calendar app needs, like the last Friday of each month. This covers the weekly kinds a class or a team uses.
- Choosing days with no session to repeat. Use choice chips.

## Props

- `start`: `string`. The first session, "2026-10-06". Required.
- `time`: `string`. Said back in the series, like "19:00 to 20:30".
- `seasonEnd`: `string`. The season's last day, "2027-05-15". Offers "End of season", picked by default.
- `closed`: `{ from: string; to: string; reason: string }[]`. Days the session can't happen, as inclusive ranges with why, like "Room 2 is closed for repainting".
- `value`: `{ every: "none" | "week" | "two-weeks"; days?: number[]; until?: string | null; skip?: string[] }`. The answers. days are weekdays, 0 is Monday. until is a day, "season", or null to ask. skip lists days left out. Use with onValueChange to control it.
- `defaultValue`: `RepeatValue`. The answers at first render, when you do not control it. Default once.
- `onValueChange`: `(value: RepeatValue) => void`. Called with the answers on each change, skips too.
- `label`: `ReactNode`. Default `"Repeats"`. Names the first question.
- `size`: `"sm" | "md" | "lg"`. Default `"md"`. Text, card, and chip size.
- `disabled`: `boolean`. Default `false`. Turns every question off.
- `locale`: `string`. Default `"en-GB"`. A language tag for days and dates, which reads "Sat 15 May".
- `className`: `string`. Classes for the outer box.

## Accessibility

- How often is a radio group named by the label. The arrow keys move and choose.
- The days are a group of checkboxes named "On". Until is a radio group, and its date opens a calendar.
- Each clash says its status in words, "3 sessions clash", then why. Its dates show on hover, focus, or tap.
- Skip them moves focus to the series it changed, and a polite status says how many were skipped.
- ⌘Z puts them back, and the page says so once.

## Examples

### Weekly to the season's end, with a closure

```tsx
<RepeatField
  start="2026-10-06"
  time="19:00 to 20:30"
  seasonEnd="2027-05-15"
  closed={[{ from: "2026-10-13", to: "2026-10-20", reason: "Room 2 is closed for repainting" }]}
  defaultValue={{ every: "week", days: [1, 3], until: "season" }}
/>
```

### Once, until it repeats

```tsx
<RepeatField start="2026-10-06" time="10:00 to 11:00" />
```
