@blakesteve/roster 5.16.0 → 5.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +227 -2
  2. package/dist/components/molecules/RadioTileGroup/RadioTileGroup.cjs +1 -0
  3. package/dist/components/molecules/RadioTileGroup/RadioTileGroup.d.ts +101 -0
  4. package/dist/components/molecules/RadioTileGroup/RadioTileGroup.js +142 -0
  5. package/dist/components/molecules/RadioTileGroup/radio-tile-group-variants.cjs +1 -0
  6. package/dist/components/molecules/RadioTileGroup/radio-tile-group-variants.d.ts +49 -0
  7. package/dist/components/molecules/RadioTileGroup/radio-tile-group-variants.js +34 -0
  8. package/dist/components/molecules/StepProgress/StepProgress.cjs +1 -1
  9. package/dist/components/molecules/StepProgress/StepProgress.js +29 -42
  10. package/dist/components/molecules/Timeline/Timeline.cjs +1 -0
  11. package/dist/components/molecules/Timeline/Timeline.d.ts +86 -0
  12. package/dist/components/molecules/Timeline/Timeline.js +217 -0
  13. package/dist/components/organisms/Dialog/Dialog.cjs +1 -1
  14. package/dist/components/organisms/Dialog/Dialog.d.ts +58 -6
  15. package/dist/components/organisms/Dialog/Dialog.js +82 -59
  16. package/dist/components/organisms/Sheet/Sheet.cjs +1 -1
  17. package/dist/components/organisms/Sheet/Sheet.d.ts +2 -1
  18. package/dist/components/organisms/Sheet/Sheet.js +90 -87
  19. package/dist/index.d.ts +3 -0
  20. package/dist/internal/leave-watchdog.cjs +1 -0
  21. package/dist/internal/leave-watchdog.d.ts +62 -0
  22. package/dist/internal/leave-watchdog.js +64 -0
  23. package/dist/internal/step-check.cjs +1 -0
  24. package/dist/internal/step-check.d.ts +2 -0
  25. package/dist/internal/step-check.js +6 -0
  26. package/dist/internal/step-marker.cjs +1 -0
  27. package/dist/internal/step-marker.d.ts +10 -0
  28. package/dist/internal/step-marker.js +12 -0
  29. package/dist/meta.json +3 -3
  30. package/dist/roster.cjs +1 -1
  31. package/dist/roster.css +1 -1
  32. package/dist/roster.js +155 -147
  33. package/package.json +1 -1
package/README.md CHANGED
@@ -896,6 +896,50 @@ bundled: its queue lives in module scope, so a bundled copy would give you two
896
896
  stores, and toasts fired from your own `import { toast } from "react-hot-toast"`
897
897
  would silently never appear.
898
898
 
899
+ **What mounting it costs.** `Toaster` brings react-hot-toast's own
900
+ `Toaster`, Roster's `Toast` with its icons, and Font Awesome's runtime;
901
+ `toast` alone is about 3.7 KB. Measured against 5.17.0's build (esbuild,
902
+ minified, gzipped), mounting it adds about 34 KB to a page that uses nothing
903
+ else from Roster, 25 KB beside a `Button`, and under 4 KB beside a `Dialog`,
904
+ which already carries Font Awesome. Mounted in a root layout, every page pays
905
+ that for a message most visits never see.
906
+
907
+ To pay only when there's something to say, mount it on the first toast. The
908
+ queue is react-hot-toast's module-level store, so a toast raised while the
909
+ host is still loading waits there and shows when it arrives:
910
+
911
+ ```tsx
912
+ // toast-host.tsx: loaded on the first toast, never before.
913
+ import { Toaster } from "@blakesteve/roster";
914
+ export default function ToastHost() {
915
+ return <Toaster position="bottom-right" />;
916
+ }
917
+ ```
918
+
919
+ ```ts
920
+ // notify.ts: call these instead of `toast` directly.
921
+ import { createElement } from "react";
922
+ import { createRoot } from "react-dom/client";
923
+ import { toast } from "@blakesteve/roster";
924
+
925
+ let host: Promise<void> | null = null;
926
+ function mountHost() {
927
+ host ??= import("./toast-host").then(({ default: ToastHost }) => {
928
+ const el = document.createElement("div");
929
+ document.body.append(el);
930
+ createRoot(el).render(createElement(ToastHost));
931
+ });
932
+ }
933
+
934
+ export const notify = {
935
+ success: (message: string) => (mountHost(), toast.success(message)),
936
+ error: (message: string) => (mountHost(), toast.error(message)),
937
+ };
938
+ ```
939
+
940
+ Mounted in its own root like this, it follows a `.dark` on `<html>` but not
941
+ one scoped to part of your app.
942
+
899
943
  An app already using it keeps its call sites and only changes the import. What changes is
900
944
  the styling: the toast body is a real `Toast` component reading `--roster-*`
901
945
  rather than the inline hex each app was pinning.
@@ -1284,6 +1328,12 @@ rather than a rewrite.
1284
1328
  short scannable set that would otherwise spend three rows of a wide form. It
1285
1329
  sets `aria-orientation` to match.
1286
1330
 
1331
+ `disabled` dims the whole group: its label, every option and the helper text.
1332
+ The dimmed text measures under 4.5:1, and a contrast checker such as axe will
1333
+ say so. That's on purpose: WCAG 1.4.3 exempts text that's part of an inactive
1334
+ control, and a disabled group is one. The same goes for `CheckboxGroup` and
1335
+ `RadioTileGroup`.
1336
+
1287
1337
  Every radio is a 44x44 target, and the component is built so that it stays one:
1288
1338
  each option's box is sized to contain its own target, which is why the options
1289
1339
  can sit close together without a target ever reaching into its neighbor.
@@ -1296,6 +1346,128 @@ escape the box. Lay the options out across with `orientation` rather than
1296
1346
  through `optionsClassName`, for the same reason: the horizontal spacing that
1297
1347
  keeps two radios 44px apart rides on that prop.
1298
1348
 
1349
+ ### One answer, chosen by its picture
1350
+
1351
+ `RadioTileGroup` is `RadioGroup` with tiles for options: a picture, a name and
1352
+ an optional description each, and no radio dot. For choosing between photos,
1353
+ avatars, swatches or layouts, where people know the options by sight. It's
1354
+ the same radio group to a keyboard and a screen reader: one tab stop, the
1355
+ arrows move and choose (wrapping, and skipping a disabled tile), Space
1356
+ chooses, and Home and End go to the first and last tile. `value`, `onChange`,
1357
+ `label`, `helperText`, `errorMessage` and `disabled` work as in `RadioGroup`,
1358
+ and `required` sets `aria-required`.
1359
+
1360
+ ```tsx
1361
+ <RadioTileGroup
1362
+ label="Cover photo"
1363
+ value={cover}
1364
+ onChange={setCover}
1365
+ options={[
1366
+ { value: "harbor", label: "Harbor at dawn", picture: <img src={harbor} alt="" /> },
1367
+ { value: "ridge", label: "Pine ridge", description: "Fog in the valley", picture: <img src={ridge} alt="" /> },
1368
+ { value: "flats", label: "Salt flats", picture: <img src={flats} alt="" /> },
1369
+ ]}
1370
+ />
1371
+ ```
1372
+
1373
+ A tile is named by its `label` alone. The picture is hidden from assistive
1374
+ technology so the name isn't read twice, which is why the images above have
1375
+ `alt=""`.
1376
+
1377
+ The arrows follow the tiles' order, not the grid's rows: Down is the next
1378
+ tile, as Right is. A row is however many tiles fit the width, so moving by
1379
+ row would change with the window, and the radio pattern a screen reader
1380
+ announces says Down is next.
1381
+
1382
+ **Laying it out.** Two tiles to a row, then three from the `sm` breakpoint.
1383
+ `columns` takes one count, or a count per breakpoint, where one left out takes
1384
+ the count below it: `columns={{ base: 2, md: 4 }}`. `minTileWidth="9rem"`
1385
+ instead fits as many tiles to a row as the group's own width holds, whatever
1386
+ the viewport. In the default `outlined` look an `<img>` placed directly in the
1387
+ picture is cropped square, so photos of any shape make even rows; anything
1388
+ else keeps its own shape, and `pictureClassName` changes either.
1389
+
1390
+ **Drawing your own chosen look.** Each tile, the element with `role="radio"`,
1391
+ carries `data-checked`, `data-disabled`, `data-hover` and `data-focus-visible`,
1392
+ and takes `tileClassName`. `variant="plain"` drops Roster's edge, fill and
1393
+ padding, so there's nothing to undo before drawing a frame or a stamp of your
1394
+ own. Roster's styles sit in a cascade layer below yours, so plain CSS wins
1395
+ without `!important`:
1396
+
1397
+ ```css
1398
+ .cover [data-checked] .frame { box-shadow: inset 0 0 0 3px #1c1917; }
1399
+ .cover [data-checked] .stamp { opacity: 1; }
1400
+ ```
1401
+
1402
+ The focus ring stays Roster's in both looks, offset outside the tile, so
1403
+ it's outside anything drawn on the tile or inside it.
1404
+
1405
+ **Tokens.** The `outlined` look reads `--roster-tile-border` and
1406
+ `--roster-tile-bg` at rest, and `--roster-tile-border-checked` and
1407
+ `--roster-tile-bg-checked` on the chosen tile, whose edge is drawn two pixels
1408
+ wide so it differs in weight as well as color. Light and dark values ship in
1409
+ `roster.css`.
1410
+
1411
+ ### Dialog
1412
+
1413
+ A modal dialog. Focus moves in when it opens, Tab stays inside, Escape and its
1414
+ close button close it, focus goes back to what opened it, and the page behind
1415
+ is inert and hidden from assistive technology (a toast still gets through: see
1416
+ [Toasts](#toasts)). `onAfterClose` runs once it has finished closing.
1417
+
1418
+ **A close always leaves.** A close that cuts into the entrance could, now and
1419
+ then, leave the dialog mounted and invisible over the page, taking every
1420
+ click: Headless UI's transition stopped waiting before the leave had ended.
1421
+ Roster now removes a dialog still there after its leave and a short margin,
1422
+ and `onAfterClose` still runs once. It also waits for the leave: under load,
1423
+ Headless UI could report a leave over before it had run, and `onAfterClose`
1424
+ came with the dialog still on screen. Nothing to change in your code; `Sheet`
1425
+ gets the same. Headless UI tracks the cause in
1426
+ [#3902](https://github.com/tailwindlabs/headlessui/issues/3902), and
1427
+ [#3895](https://github.com/tailwindlabs/headlessui/pull/3895) would fix the
1428
+ stuck dialog: Roster's removal comes out once a Headless UI release carries
1429
+ that fix.
1430
+
1431
+ **The dialog has a box.** The element carrying `role="dialog"` is the
1432
+ viewport's box, in either layout, so a test that finds the dialog by its role
1433
+ and checks it's visible (Playwright's `toBeVisible`, say) finds it open. In
1434
+ 5.16.0 and before, the default layout's was a wrapper with no height, which
1435
+ read as hidden; `Sheet`'s was too, and is fixed the same way. Nothing on
1436
+ screen moves, and a click outside the panel still closes it.
1437
+
1438
+ **Full screen.** `layout="fullscreen"` makes a bare dialog over the whole
1439
+ viewport, for a photo viewer or a full-screen card: no box, no padding, and no
1440
+ header or close button of Roster's. Everything inside is yours, a close
1441
+ included; Roster keeps the focus, the keys and the page behind. `titleHidden`
1442
+ keeps the title as the dialog's accessible name, announced when it opens,
1443
+ without showing it.
1444
+
1445
+ ```tsx
1446
+ <Dialog isOpen={open} onClose={close} title="Photos" titleHidden layout="fullscreen">
1447
+ <div style={{ height: "100%", display: "grid", placeItems: "center" }}>
1448
+ <img src={photo.src} alt={photo.alt} />
1449
+ <p>{index + 1} of {photos.length}: {photo.caption}</p>
1450
+ <Button onClick={previous}>Previous</Button>
1451
+ <Button onClick={next}>Next</Button>
1452
+ <Button autoFocus onClick={close}>Close</Button>
1453
+ </div>
1454
+ </Dialog>
1455
+ ```
1456
+
1457
+ - **The dialog is the viewport.** `className` lands on the full-viewport
1458
+ panel. A click anywhere in it doesn't close it: give it your own close.
1459
+ - **The scrim stays,** retinted by `--roster-backdrop`, and it fades in and
1460
+ out without scaling.
1461
+ - **`size` and `status` don't apply;** `variant` still picks the scrim.
1462
+ - **A title that isn't hidden** is Roster's `h2`, first in the panel. Most
1463
+ full-screen dialogs name themselves on screen and hide it.
1464
+ - **`titleHidden` works with the default layout too:** the header keeps its
1465
+ close button.
1466
+
1467
+ **Where focus starts.** On the element marked `data-autofocus`, or a Roster
1468
+ `Button` given `autoFocus` (Headless UI reads both). With neither, on the
1469
+ dialog itself. In a photo viewer that's usually Close.
1470
+
1299
1471
  ### ModerationQueue
1300
1472
 
1301
1473
  A queue of things waiting for a decision: submissions to approve, reports to
@@ -1628,7 +1800,8 @@ label and value on one baseline above the bar, an optional detail line below.
1628
1800
  numbered marker and connectors that fill behind the current one;
1629
1801
  `"segmented"` is the compact form, always with "Step 2 of 5". The current
1630
1802
  step is `aria-current="step"`, finished ones say "done", and the position is
1631
- said when it changes.
1803
+ said when it changes. For steps down the page with content of their own, see
1804
+ [Timeline](#timeline).
1632
1805
 
1633
1806
  **LoadingDots.** Only for a model generating before its first token. It takes
1634
1807
  the surrounding text's size and color, rests at 1, 0.6 and 0.3 opacity (also
@@ -1637,6 +1810,56 @@ shown beside it.
1637
1810
 
1638
1811
  Every string in all five is a prop with an English default.
1639
1812
 
1813
+ ### Timeline
1814
+
1815
+ Steps down the page, each with content of its own: a schedule, a history, an
1816
+ onboarding list. Every step has a `title`, any `content` under it, a leading
1817
+ `when` column, a trailing `actions` column, and a marker on a line that runs
1818
+ through them all: its number, a check once done, `"hollow"` for an empty step,
1819
+ or a mark of your own.
1820
+
1821
+ ```tsx
1822
+ <Timeline
1823
+ label="The course, week by week"
1824
+ current={3}
1825
+ nowBefore={4}
1826
+ labels={{ now: "Today" }}
1827
+ steps={[
1828
+ { id: "w1", when: "Week 1 · Sep 2", title: "Soil and compost", actions: <Button size="sm">Edit</Button> },
1829
+ { id: "w2", when: "Week 2 · Sep 9", title: "Sowing in rows", content: "Depth, spacing and labels." },
1830
+ { id: "w3", when: "Week 3 · Sep 16", title: "Water and mulch" },
1831
+ { id: "w4", when: "Week 4 · Sep 23", title: "No topic yet", marker: "hollow" },
1832
+ ]}
1833
+ />
1834
+ ```
1835
+
1836
+ `current` is the step you're on, from 1: the steps before it are done, and
1837
+ past the last means every step is. `nowBefore` draws a "now" divider just
1838
+ before a step, where today falls, which needn't be beside the current step.
1839
+
1840
+ Every column comes from one grid, so the columns line up from step to step
1841
+ whatever each step holds, and a column no step uses takes no room. Below 40rem
1842
+ of its own width (not the window's), the `when` moves above the body and the
1843
+ actions under it.
1844
+
1845
+ It's an ordered list with a name (`label`, or `aria-labelledby` for a heading
1846
+ on the page). The current step is `aria-current="step"`, and a finished one
1847
+ says "done" after its title. The markers, the line and the divider are
1848
+ decoration, hidden from assistive technology. It takes no keys: a step's
1849
+ buttons and links are reached in reading order, which is the order they show.
1850
+
1851
+ Each `<li>` carries `data-step` (`done`, `current` or `upcoming`), and every
1852
+ part takes a class of its own. A mark of your own needs a background, or the
1853
+ line shows through it. The line reads `--roster-timeline-rail` ahead and
1854
+ `--roster-timeline-rail-done` behind the current step, and the divider
1855
+ `--roster-timeline-now`. In forced colors they become GrayText, Highlight and
1856
+ CanvasText.
1857
+
1858
+ **Why not `StepProgress orientation="vertical"`.** A timeline isn't always
1859
+ progress: a schedule doesn't advance while you watch it, so there's no "Step 3
1860
+ of 5" status here, and each step carries content where `StepProgress` takes
1861
+ names. The two share their markers.
1862
+
1640
1863
  ### Menu
1641
1864
 
1642
1865
  A button that opens a panel of actions: the menu button pattern. Compose it:
@@ -2327,8 +2550,10 @@ function App() {
2327
2550
  | `SkeletonCard`, `SkeletonTableRow`, `SkeletonAvatar`, `SkeletonStat` | Skeleton presets in the real component's box |
2328
2551
  | `ProgressField` | Progress with a label and value on one baseline and a detail line: the form to reach for |
2329
2552
  | `StepProgress` | Named steps: a stepper with markers and connectors, or a compact segmented "Step x of y" |
2553
+ | `Timeline` | Steps down the page with content of their own: a when column, a marker on a line, the body and actions in one grid; a "now" divider (see [Timeline](#timeline)) |
2330
2554
  | `Pullquote` | A line lifted out of prose, as `<figure>` + `<blockquote>` + `<figcaption>` |
2331
2555
  | `RadioGroup` | Radio set reporting one `string`; descriptions, error, 44px targets |
2556
+ | `RadioTileGroup` | A radio group of picture tiles: one tab stop, the arrows, Home and End; `data-checked` for a chosen look of your own (see [One answer, chosen by its picture](#one-answer-chosen-by-its-picture)) |
2332
2557
  | `Slider` | A value along a track, starting in the middle or not set on request; named snap-point chips, a visible number, the full keyboard, 44px targets (see [Slider](#slider)) |
2333
2558
  | `RangeSlider` | A low and a high on one track: two thumbs that meet and never cross, each its own slider to the keyboard (see [RangeSlider](#rangeslider)) |
2334
2559
  | `Tabs` | Tab strip, plain (an underline) or liquid (a sliding pill); arrow keys, one tab stop, counts, 44px `lg` size (see [Tabs](#tabs)) |
@@ -2342,7 +2567,7 @@ function App() {
2342
2567
  | `ActionBar` | Sticky bottom action strip |
2343
2568
  | `Countdown` | Live countdown timer |
2344
2569
  | `DataTable` | Table with sorting and paging via TanStack Table v9. Its pager is built on `Pagination` (since 5.6.0, the arrows layout), worded through `paginationLabels`; the page size is fixed at 10, with no prop to change it yet. Imported from `@blakesteve/roster/data-table` (see [DataTable columns](#datatable-columns)) |
2345
- | `Dialog` | Modal dialog; `onAfterClose` runs once it has finished closing |
2570
+ | `Dialog` | Modal dialog; `onAfterClose` runs once it has finished closing; a bare full-screen layout with a hidden title (see [Dialog](#dialog)) |
2346
2571
  | `Footer` | Site footer |
2347
2572
  | `ModerationQueue` | Items waiting for a decision: busy per action, confirm, reason and edit steps, focus after an item leaves, a live region. Never fetches (see [ModerationQueue](#moderationqueue)) |
2348
2573
  | `QueueEditDialog` | A ModerationQueue edit step in a Dialog: pass it as an edit's `surface`. A separate export, so a queue that never uses it doesn't ship it |
@@ -0,0 +1 @@
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),j=require("react"),b=require("../../../node_modules/@headlessui/react/dist/components/description/description.cjs"),S=require("../../../node_modules/@headlessui/react/dist/components/field/field.cjs"),h=require("../../../node_modules/@headlessui/react/dist/components/label/label.cjs"),g=require("../../../node_modules/@headlessui/react/dist/components/radio-group/radio-group.cjs"),s=require("../../../lib/utils.cjs"),k=require("../../../internal/field-invalid.cjs"),z=require("../../../internal/key-focus.cjs"),m=require("./radio-tile-group-variants.cjs"),A=j.forwardRef(function(r,i){return t.jsx("div",{ref:i,...r,"data-focus-visible":r["data-focus"]})});function B(a){const r=typeof a=="number"?{base:a}:a,i=r.base??1,l=r.sm??i,o=r.md??l,n=r.lg??o;return{"--rst-tiles-cols":i,"--rst-tiles-cols-sm":l,"--rst-tiles-cols-md":o,"--rst-tiles-cols-lg":n}}const C=({options:a,value:r,onChange:i,label:l,helperText:o,errorMessage:n,disabled:u=!1,required:q=!1,variant:w="outlined",columns:N={base:2,sm:3},minTileWidth:d,className:R,optionsClassName:G,tileClassName:T,pictureClassName:D,labelClassName:P,optionLabelClassName:V,descriptionClassName:F,messageClassName:I,onKeyDown:K,"aria-invalid":E,...v})=>{z.useKeyFocus();const p=!!n,H=k.isFieldInvalid(p,{...v,"aria-invalid":E}),L=k.withoutInvalidProps(v),y=w??"outlined",f=j.useRef(new Map),x=u?[]:a.filter(e=>!e.disabled),O=e=>{if(K?.(e),e.defaultPrevented||e.key!=="Home"&&e.key!=="End")return;const c=e.key==="Home"?x[0]:x[x.length-1];c&&(e.preventDefault(),f.current.get(c.value)?.focus(),c.value!==r&&i(c.value))};return t.jsxs(g.RadioGroup,{value:r,onChange:i,disabled:u,className:s.cn("rst:font-ui rst:flex rst:flex-col rst:gap-1.5 rst:min-w-0",R),...L,onKeyDown:O,"aria-invalid":H||void 0,"aria-required":q||void 0,children:[l&&t.jsx(h.Label,{className:s.cn("rst:block rst:text-sm rst:font-medium rst:text-[var(--roster-control-text)] rst:text-left",u&&"rst:opacity-50",P),children:l}),t.jsx("div",{className:s.cn(m.radioTileGroupOptionsVariants({fit:d?"width":"columns"}),G),style:d?{"--rst-tiles-min":d}:B(N),children:a.map(e=>t.jsx(S.Field,{disabled:u||e.disabled,className:"rst:flex rst:min-w-0",children:t.jsxs(g.Radio,{as:A,value:e.value,ref:c=>{c?f.current.set(e.value,c):f.current.delete(e.value)},className:s.cn(m.radioTileVariants({variant:y}),T),children:[e.picture!=null&&t.jsx("span",{"aria-hidden":"true",className:s.cn(m.radioTilePictureVariants({variant:y}),D),children:e.picture}),t.jsx(h.Label,{as:"span",passive:!0,className:s.cn("rst:block rst:text-sm rst:font-medium rst:text-[var(--roster-control-text)] rst:[overflow-wrap:anywhere]","rst:forced-colors:group-data-checked:underline rst:forced-colors:group-data-checked:decoration-2 rst:forced-colors:group-data-checked:underline-offset-4",V),children:e.label}),e.description&&t.jsx(b.Description,{as:"span",className:s.cn("rst:block rst:text-xs rst:text-gray-500 rst:dark:text-gray-400",F),children:e.description})]})},e.value))}),(o||n)&&t.jsx(b.Description,{as:"p",className:s.cn("rst:text-xs rst:text-left",p?"rst:text-error-600 rst:dark:text-error-400 rst:font-medium":"rst:text-gray-500 rst:dark:text-gray-400",u&&"rst:opacity-50",I),children:n||o})]})};exports.RadioTileGroup=C;
@@ -0,0 +1,101 @@
1
+ import { default as React } from 'react';
2
+ import { VariantProps } from 'class-variance-authority';
3
+ import { radioTileVariants } from './radio-tile-group-variants';
4
+ export type RadioTileOption = {
5
+ value: string;
6
+ /** The option's name, under its picture. It's what the radio is called. */
7
+ label: string;
8
+ /** Supporting text under the name, read as the radio's description. */
9
+ description?: string;
10
+ /**
11
+ * What the tile shows above its name: a photo, a drawing, a swatch, any
12
+ * node. Hidden from assistive technology, since `label` already names the
13
+ * option, so the name isn't read twice. Give an image `alt=""`.
14
+ */
15
+ picture?: React.ReactNode;
16
+ disabled?: boolean;
17
+ };
18
+ /**
19
+ * How many tiles to a row: one count, or a count per breakpoint. A
20
+ * breakpoint left out takes the count of the one below it, and `base` is 1
21
+ * when it's left out.
22
+ */
23
+ export type RadioTileColumns = number | {
24
+ base?: number;
25
+ sm?: number;
26
+ md?: number;
27
+ lg?: number;
28
+ };
29
+ export interface RadioTileGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "defaultValue">, Pick<VariantProps<typeof radioTileVariants>, "variant"> {
30
+ options: RadioTileOption[];
31
+ /**
32
+ * The selected value. A value matching no option selects nothing, and
33
+ * leaves the first option as the group's tab stop: `""` is the way to say
34
+ * "not answered yet", as in `RadioGroup`.
35
+ */
36
+ value: string;
37
+ onChange: (value: string) => void;
38
+ /** The group's name. Becomes the group's accessible name. */
39
+ label?: string;
40
+ /** Supporting text under the tiles. */
41
+ helperText?: string;
42
+ /**
43
+ * The message for an invalid selection. Implies the error state, and is the
44
+ * group's description while it shows, as in `RadioGroup`, which says how
45
+ * `aria-invalid` and `aria-describedby` are handled.
46
+ */
47
+ errorMessage?: string;
48
+ /** Disables every tile, and dims the label and helper text with them. */
49
+ disabled?: boolean;
50
+ /** Says the group needs an answer, as `aria-required`. */
51
+ required?: boolean;
52
+ /**
53
+ * Tiles per row. Two, then three from the `sm` breakpoint, by default:
54
+ * two photos side by side is the useful size on a phone.
55
+ */
56
+ columns?: RadioTileColumns;
57
+ /**
58
+ * A tile's narrowest width, as a CSS length (`"9rem"`). Set, it replaces
59
+ * `columns`: each row holds as many tiles as fit the group's own width,
60
+ * whatever the viewport.
61
+ */
62
+ minTileWidth?: string;
63
+ /** Classes for the grid holding the tiles. `className` lands on the group. */
64
+ optionsClassName?: string;
65
+ /**
66
+ * Classes for every tile, the element carrying `role="radio"`. It carries
67
+ * `data-checked`, `data-disabled`, `data-hover` and `data-focus-visible` as
68
+ * well, so a chosen look can be drawn from them, with `variant="plain"` for
69
+ * none of Roster's own.
70
+ */
71
+ tileClassName?: string;
72
+ /** Classes for what holds each option's `picture`. */
73
+ pictureClassName?: string;
74
+ /** Classes for the group's own label. */
75
+ labelClassName?: string;
76
+ /** Classes for each option's name. */
77
+ optionLabelClassName?: string;
78
+ /** Classes for each option's `description`. */
79
+ descriptionClassName?: string;
80
+ /** Classes for the helper text or error message under the tiles. */
81
+ messageClassName?: string;
82
+ }
83
+ /**
84
+ * A radio group whose options are tiles: a picture, a name and an optional
85
+ * description each, with no radio dot. Choosing between photos, avatars,
86
+ * swatches or layouts.
87
+ *
88
+ * A sibling of `RadioGroup` rather than a variant of it. Built on the same
89
+ * Headless UI pieces, so it's the same radio group to a keyboard and a screen
90
+ * reader: one tab stop, the arrows moving and choosing (wrapping, skipping
91
+ * disabled tiles), Space choosing. It adds Home and End, to the first and
92
+ * last tile. `RadioGroup` keeps its own props, which are about dots and rows,
93
+ * and a form with only text radios doesn't carry the tiles' code.
94
+ *
95
+ * The arrows follow the tiles' order, not the grid's rows: Down moves to the
96
+ * next tile, as Right does, the order the tiles are read in. A row is however
97
+ * many tiles fit the width, so moving by row would change with the window,
98
+ * and the radio pattern a screen reader announces says Down is next.
99
+ */
100
+ declare const RadioTileGroup: ({ options, value, onChange, label, helperText, errorMessage, disabled, required, variant, columns, minTileWidth, className, optionsClassName, tileClassName, pictureClassName, labelClassName, optionLabelClassName, descriptionClassName, messageClassName, onKeyDown, "aria-invalid": ariaInvalid, ...props }: RadioTileGroupProps) => import("react/jsx-runtime").JSX.Element;
101
+ export { RadioTileGroup };
@@ -0,0 +1,142 @@
1
+ "use client";
2
+ import { jsxs as y, jsx as e } from "react/jsx-runtime";
3
+ import k from "react";
4
+ import { Description as h } from "../../../node_modules/@headlessui/react/dist/components/description/description.js";
5
+ import { Field as q } from "../../../node_modules/@headlessui/react/dist/components/field/field.js";
6
+ import { Label as g } from "../../../node_modules/@headlessui/react/dist/components/label/label.js";
7
+ import { RadioGroup as L, Radio as O } from "../../../node_modules/@headlessui/react/dist/components/radio-group/radio-group.js";
8
+ import { cn as s } from "../../../lib/utils.js";
9
+ import { isFieldInvalid as Z, withoutInvalidProps as z } from "../../../internal/field-invalid.js";
10
+ import { useKeyFocus as A } from "../../../internal/key-focus.js";
11
+ import { radioTilePictureVariants as B, radioTileVariants as C, radioTileGroupOptionsVariants as J } from "./radio-tile-group-variants.js";
12
+ const M = k.forwardRef(function(t, o) {
13
+ return /* @__PURE__ */ e("div", { ref: o, ...t, "data-focus-visible": t["data-focus"] });
14
+ });
15
+ function Q(a) {
16
+ const t = typeof a == "number" ? { base: a } : a, o = t.base ?? 1, l = t.sm ?? o, c = t.md ?? l, n = t.lg ?? c;
17
+ return {
18
+ "--rst-tiles-cols": o,
19
+ "--rst-tiles-cols-sm": l,
20
+ "--rst-tiles-cols-md": c,
21
+ "--rst-tiles-cols-lg": n
22
+ };
23
+ }
24
+ const ar = ({
25
+ options: a,
26
+ value: t,
27
+ onChange: o,
28
+ label: l,
29
+ helperText: c,
30
+ errorMessage: n,
31
+ disabled: d = !1,
32
+ required: w = !1,
33
+ variant: b = "outlined",
34
+ columns: N = { base: 2, sm: 3 },
35
+ minTileWidth: u,
36
+ className: R,
37
+ optionsClassName: D,
38
+ tileClassName: G,
39
+ pictureClassName: K,
40
+ labelClassName: P,
41
+ optionLabelClassName: V,
42
+ descriptionClassName: F,
43
+ messageClassName: T,
44
+ onKeyDown: j,
45
+ "aria-invalid": E,
46
+ ...p
47
+ }) => {
48
+ A();
49
+ const x = !!n, H = Z(x, { ...p, "aria-invalid": E }), I = z(p), v = b ?? "outlined", m = k.useRef(/* @__PURE__ */ new Map()), f = d ? [] : a.filter((r) => !r.disabled), _ = (r) => {
50
+ if (j?.(r), r.defaultPrevented || r.key !== "Home" && r.key !== "End") return;
51
+ const i = r.key === "Home" ? f[0] : f[f.length - 1];
52
+ i && (r.preventDefault(), m.current.get(i.value)?.focus(), i.value !== t && o(i.value));
53
+ };
54
+ return /* @__PURE__ */ y(
55
+ L,
56
+ {
57
+ value: t,
58
+ onChange: o,
59
+ disabled: d,
60
+ className: s("rst:font-ui rst:flex rst:flex-col rst:gap-1.5 rst:min-w-0", R),
61
+ ...I,
62
+ onKeyDown: _,
63
+ "aria-invalid": H || void 0,
64
+ "aria-required": w || void 0,
65
+ children: [
66
+ l && /* @__PURE__ */ e(
67
+ g,
68
+ {
69
+ className: s(
70
+ "rst:block rst:text-sm rst:font-medium rst:text-[var(--roster-control-text)] rst:text-left",
71
+ d && "rst:opacity-50",
72
+ P
73
+ ),
74
+ children: l
75
+ }
76
+ ),
77
+ /* @__PURE__ */ e(
78
+ "div",
79
+ {
80
+ className: s(J({ fit: u ? "width" : "columns" }), D),
81
+ style: u ? { "--rst-tiles-min": u } : Q(N),
82
+ children: a.map((r) => (
83
+ /* `disabled || option.disabled`, as in `RadioGroup`: Headless UI
84
+ takes the group's state only when the option's is `undefined`. */
85
+ /* @__PURE__ */ e(q, { disabled: d || r.disabled, className: "rst:flex rst:min-w-0", children: /* @__PURE__ */ y(
86
+ O,
87
+ {
88
+ as: M,
89
+ value: r.value,
90
+ ref: (i) => {
91
+ i ? m.current.set(r.value, i) : m.current.delete(r.value);
92
+ },
93
+ className: s(C({ variant: v }), G),
94
+ children: [
95
+ r.picture != null && /* @__PURE__ */ e("span", { "aria-hidden": "true", className: s(B({ variant: v }), K), children: r.picture }),
96
+ /* @__PURE__ */ e(
97
+ g,
98
+ {
99
+ as: "span",
100
+ passive: !0,
101
+ className: s(
102
+ "rst:block rst:text-sm rst:font-medium rst:text-[var(--roster-control-text)] rst:[overflow-wrap:anywhere]",
103
+ "rst:forced-colors:group-data-checked:underline rst:forced-colors:group-data-checked:decoration-2 rst:forced-colors:group-data-checked:underline-offset-4",
104
+ V
105
+ ),
106
+ children: r.label
107
+ }
108
+ ),
109
+ r.description && /* @__PURE__ */ e(
110
+ h,
111
+ {
112
+ as: "span",
113
+ className: s("rst:block rst:text-xs rst:text-gray-500 rst:dark:text-gray-400", F),
114
+ children: r.description
115
+ }
116
+ )
117
+ ]
118
+ }
119
+ ) }, r.value)
120
+ ))
121
+ }
122
+ ),
123
+ (c || n) && /* @__PURE__ */ e(
124
+ h,
125
+ {
126
+ as: "p",
127
+ className: s(
128
+ "rst:text-xs rst:text-left",
129
+ x ? "rst:text-error-600 rst:dark:text-error-400 rst:font-medium" : "rst:text-gray-500 rst:dark:text-gray-400",
130
+ d && "rst:opacity-50",
131
+ T
132
+ ),
133
+ children: n || c
134
+ }
135
+ )
136
+ ]
137
+ }
138
+ );
139
+ };
140
+ export {
141
+ ar as RadioTileGroup
142
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("../../../node_modules/class-variance-authority/dist/index.cjs"),t=r.cva("rst:group rst:font-ui rst:relative rst:flex rst:h-full rst:w-full rst:min-w-0 rst:flex-col rst:gap-2 rst:rounded-lg rst:text-left rst:cursor-pointer rst:focus:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring rst:focus-visible:ring-offset-2 rst:ring-offset-background rst:data-disabled:cursor-not-allowed rst:data-disabled:opacity-50",{variants:{variant:{outlined:"rst:border rst:border-(--roster-tile-border) rst:bg-(--roster-tile-bg) rst:p-2 rst:data-checked:border-(--roster-tile-border-checked) rst:data-checked:inset-ring rst:data-checked:inset-ring-(--roster-tile-border-checked) rst:data-checked:bg-(--roster-tile-bg-checked) rst:forced-colors:data-checked:border-2",plain:""}},defaultVariants:{variant:"outlined"}}),s=r.cva("rst:block rst:w-full rst:min-w-0",{variants:{variant:{outlined:"rst:overflow-hidden rst:rounded-md rst:[&>img]:block rst:[&>img]:aspect-square rst:[&>img]:w-full rst:[&>img]:object-cover",plain:""}},defaultVariants:{variant:"outlined"}}),e=r.cva("rst:grid rst:gap-3",{variants:{fit:{columns:"rst:grid-cols-[repeat(var(--rst-tiles-cols),minmax(0,1fr))] rst:sm:grid-cols-[repeat(var(--rst-tiles-cols-sm),minmax(0,1fr))] rst:md:grid-cols-[repeat(var(--rst-tiles-cols-md),minmax(0,1fr))] rst:lg:grid-cols-[repeat(var(--rst-tiles-cols-lg),minmax(0,1fr))]",width:"rst:grid-cols-[repeat(auto-fill,minmax(min(var(--rst-tiles-min),100%),1fr))]"}},defaultVariants:{fit:"columns"}});exports.radioTileGroupOptionsVariants=e;exports.radioTilePictureVariants=s;exports.radioTileVariants=t;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * One tile: the whole tile is the radio, with no dot.
3
+ *
4
+ * `outlined` is Roster's own look, and a quiet one: a hairline in
5
+ * `--roster-tile-border` at rest, and the chosen tile's edge in
6
+ * `--roster-tile-border-checked`, drawn two pixels wide (the border and an
7
+ * inset ring) over the faintest tint, `--roster-tile-bg-checked`. The edge
8
+ * changes weight as well as hue, so the choice doesn't rest on color alone.
9
+ *
10
+ * `plain` draws nothing: no edge, no fill, no padding, in any state. It's for
11
+ * a consumer drawing its own chosen look from the tile's `data-checked`, a
12
+ * printed border or a stamp, without first undoing Roster's.
13
+ *
14
+ * Both keep the focus ring, Roster's, on `focus-visible`. A box-shadow ring
15
+ * offset from the tile's border box, so it sits outside anything a consumer
16
+ * draws on the tile or inside it. And both dim a disabled tile.
17
+ *
18
+ * Forced colors drops box-shadows and repaints every edge and fill alike, so
19
+ * there the chosen tile's name is underlined (in both looks, set where the
20
+ * name is drawn), and the `outlined` edge goes to two pixels: the same cues
21
+ * `Pagination` gives its current page.
22
+ */
23
+ export declare const radioTileVariants: (props?: ({
24
+ variant?: "plain" | "outlined" | null | undefined;
25
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
26
+ /**
27
+ * What holds an option's `picture`.
28
+ *
29
+ * `outlined` rounds and clips it, and gives an `<img>` placed directly inside
30
+ * a square, cover-fitted box the tile's width, so photos of any shape make
31
+ * even rows. Anything else, a drawing or a swatch, keeps its own shape. `plain`
32
+ * neither clips nor shapes: a consumer's frame, and a stamp hung over its
33
+ * corner, show whole.
34
+ */
35
+ export declare const radioTilePictureVariants: (props?: ({
36
+ variant?: "plain" | "outlined" | null | undefined;
37
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
38
+ /**
39
+ * The tiles' grid.
40
+ *
41
+ * `columns` reads a count per breakpoint from custom properties the component
42
+ * sets, so any count works without a class per number. `width` fills each row
43
+ * with as many tiles as fit at a minimum width, which follows the space the
44
+ * group is given rather than the viewport, and never lets a tile overflow a
45
+ * container narrower than that minimum.
46
+ */
47
+ export declare const radioTileGroupOptionsVariants: (props?: ({
48
+ fit?: "columns" | "width" | null | undefined;
49
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
@@ -0,0 +1,34 @@
1
+ import { cva as r } from "../../../node_modules/class-variance-authority/dist/index.js";
2
+ const s = /* @__PURE__ */ r(
3
+ "rst:group rst:font-ui rst:relative rst:flex rst:h-full rst:w-full rst:min-w-0 rst:flex-col rst:gap-2 rst:rounded-lg rst:text-left rst:cursor-pointer rst:focus:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring rst:focus-visible:ring-offset-2 rst:ring-offset-background rst:data-disabled:cursor-not-allowed rst:data-disabled:opacity-50",
4
+ {
5
+ variants: {
6
+ variant: {
7
+ outlined: "rst:border rst:border-(--roster-tile-border) rst:bg-(--roster-tile-bg) rst:p-2 rst:data-checked:border-(--roster-tile-border-checked) rst:data-checked:inset-ring rst:data-checked:inset-ring-(--roster-tile-border-checked) rst:data-checked:bg-(--roster-tile-bg-checked) rst:forced-colors:data-checked:border-2",
8
+ plain: ""
9
+ }
10
+ },
11
+ defaultVariants: { variant: "outlined" }
12
+ }
13
+ ), e = /* @__PURE__ */ r("rst:block rst:w-full rst:min-w-0", {
14
+ variants: {
15
+ variant: {
16
+ outlined: "rst:overflow-hidden rst:rounded-md rst:[&>img]:block rst:[&>img]:aspect-square rst:[&>img]:w-full rst:[&>img]:object-cover",
17
+ plain: ""
18
+ }
19
+ },
20
+ defaultVariants: { variant: "outlined" }
21
+ }), i = /* @__PURE__ */ r("rst:grid rst:gap-3", {
22
+ variants: {
23
+ fit: {
24
+ columns: "rst:grid-cols-[repeat(var(--rst-tiles-cols),minmax(0,1fr))] rst:sm:grid-cols-[repeat(var(--rst-tiles-cols-sm),minmax(0,1fr))] rst:md:grid-cols-[repeat(var(--rst-tiles-cols-md),minmax(0,1fr))] rst:lg:grid-cols-[repeat(var(--rst-tiles-cols-lg),minmax(0,1fr))]",
25
+ width: "rst:grid-cols-[repeat(auto-fill,minmax(min(var(--rst-tiles-min),100%),1fr))]"
26
+ }
27
+ },
28
+ defaultVariants: { fit: "columns" }
29
+ });
30
+ export {
31
+ i as radioTileGroupOptionsVariants,
32
+ e as radioTilePictureVariants,
33
+ s as radioTileVariants
34
+ };
@@ -1 +1 @@
1
- "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),l=require("../../../lib/utils.cjs"),h=require("../../../lib/labels.cjs"),b=require("../../atoms/Progress/Progress.cjs"),y={nav:"Progress",position:(s,c)=>`Step ${s} of ${c}`,done:"done",number:s=>String(s)},j=()=>r.jsx("svg",{viewBox:"0 0 24 24",width:"16",height:"16",fill:"none",stroke:"currentColor",strokeWidth:"3",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:r.jsx("path",{d:"M20 6 9 17l-5-5"})});function k({steps:s,current:c,variant:g="stepper",labels:f,className:p}){const o=h.withLabels(y,f),n=s.length;if(n===0)return null;const t=Math.min(Math.max(1,Math.floor(Number.isFinite(c)?c:1)),n+1),d=t>n,u=d?o.position(n,n):o.position(t,n),x=d?u:`${u}: ${s[t-1]}`;return g==="segmented"?r.jsxs("div",{className:l.cn("rst:font-ui rst:w-full rst:min-w-0",p),"data-step-progress":"segmented",children:[r.jsxs("div",{className:"rst:mb-1.5 rst:flex rst:items-baseline rst:justify-between rst:gap-3 rst:text-sm","aria-hidden":"true",children:[r.jsx("span",{className:"rst:font-medium rst:text-[var(--roster-control-text)]","data-step-position":"",children:u}),!d&&r.jsx("span",{className:"rst:min-w-0 rst:truncate rst:text-gray-600 rst:dark:text-gray-400","data-step-name":"",children:s[t-1]})]}),r.jsx(b.Progress,{variant:"segmented",segments:s.map((m,i)=>i+1<t?"done":i+1===t?"current":"remaining"),announce:!1}),r.jsx("span",{role:"status",className:"rst:sr-only","data-step-status":"",children:x})]}):r.jsxs("div",{className:l.cn("rst:font-ui rst:w-full",p),"data-step-progress":"stepper",children:[r.jsx("ol",{"aria-label":o.nav,className:"rst:m-0 rst:flex rst:w-full rst:list-none rst:p-0",children:s.map((m,i)=>{const a=i+1,e=a<t?"done":a===t?"current":"upcoming";return r.jsxs("li",{"aria-current":e==="current"?"step":void 0,className:"rst:relative rst:flex rst:min-w-0 rst:flex-1 rst:flex-col rst:items-center rst:gap-1.5 rst:text-center","data-step":e,children:[i>0&&r.jsx("span",{"aria-hidden":"true",className:l.cn("rst:absolute rst:right-1/2 rst:top-4 rst:h-0.5 rst:w-full rst:-translate-y-1/2",a<=t?"rst:bg-primary-600 rst:dark:bg-primary-400 rst:forced-colors:forced-color-adjust-none rst:forced-colors:bg-[Highlight]":"rst:bg-[var(--roster-skeleton)] rst:forced-colors:forced-color-adjust-none rst:forced-colors:bg-[GrayText]"),"data-step-connector":a<=t?"filled":"empty"}),r.jsx("span",{"aria-hidden":"true",className:l.cn("rst:relative rst:z-10 rst:flex rst:h-8 rst:w-8 rst:items-center rst:justify-center rst:rounded-full rst:text-sm rst:font-semibold rst:tabular-nums",e==="done"&&"rst:bg-primary-600 rst:text-primary-600-ink rst:dark:bg-primary-400 rst:dark:text-gray-950 rst:forced-colors:forced-color-adjust-none rst:forced-colors:bg-[Highlight] rst:forced-colors:text-[HighlightText]",e==="current"&&"rst:border-2 rst:border-primary-600 rst:bg-[var(--roster-card-bg)] rst:text-primary-600 rst:dark:border-primary-400 rst:dark:text-primary-400",e==="upcoming"&&"rst:bg-[var(--roster-skeleton)] rst:text-gray-600 rst:dark:text-gray-300 rst:forced-colors:border"),"data-step-marker":"",children:e==="done"?r.jsx(j,{}):o.number(a)}),r.jsxs("span",{className:l.cn("rst:w-full rst:px-1 rst:text-xs rst:leading-4 rst:break-words",e==="current"?"rst:font-semibold rst:text-[var(--roster-control-text)]":"rst:text-gray-600 rst:dark:text-gray-400"),children:[m,e==="done"&&r.jsx("span",{className:"rst:sr-only",children:`, ${o.done}`})]})]},`${a}-${m}`)})}),r.jsx("span",{role:"status",className:"rst:sr-only","data-step-status":"",children:x})]})}exports.StepProgress=k;
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react/jsx-runtime"),c=require("../../../lib/utils.cjs"),h=require("../../../lib/labels.cjs"),b=require("../../atoms/Progress/Progress.cjs"),j=require("../../../internal/step-marker.cjs"),y=require("../../../internal/step-check.cjs"),N={nav:"Progress",position:(r,i)=>`Step ${r} of ${i}`,done:"done",number:r=>String(r)};function v({steps:r,current:i,variant:g="stepper",labels:f,className:m}){const o=h.withLabels(N,f),e=r.length;if(e===0)return null;const t=Math.min(Math.max(1,Math.floor(Number.isFinite(i)?i:1)),e+1),d=t>e,u=d?o.position(e,e):o.position(t,e),x=d?u:`${u}: ${r[t-1]}`;return g==="segmented"?s.jsxs("div",{className:c.cn("rst:font-ui rst:w-full rst:min-w-0",m),"data-step-progress":"segmented",children:[s.jsxs("div",{className:"rst:mb-1.5 rst:flex rst:items-baseline rst:justify-between rst:gap-3 rst:text-sm","aria-hidden":"true",children:[s.jsx("span",{className:"rst:font-medium rst:text-[var(--roster-control-text)]","data-step-position":"",children:u}),!d&&s.jsx("span",{className:"rst:min-w-0 rst:truncate rst:text-gray-600 rst:dark:text-gray-400","data-step-name":"",children:r[t-1]})]}),s.jsx(b.Progress,{variant:"segmented",segments:r.map((p,l)=>l+1<t?"done":l+1===t?"current":"remaining"),announce:!1}),s.jsx("span",{role:"status",className:"rst:sr-only","data-step-status":"",children:x})]}):s.jsxs("div",{className:c.cn("rst:font-ui rst:w-full",m),"data-step-progress":"stepper",children:[s.jsx("ol",{"aria-label":o.nav,className:"rst:m-0 rst:flex rst:w-full rst:list-none rst:p-0",children:r.map((p,l)=>{const n=l+1,a=n<t?"done":n===t?"current":"upcoming";return s.jsxs("li",{"aria-current":a==="current"?"step":void 0,className:"rst:relative rst:flex rst:min-w-0 rst:flex-1 rst:flex-col rst:items-center rst:gap-1.5 rst:text-center","data-step":a,children:[l>0&&s.jsx("span",{"aria-hidden":"true",className:c.cn("rst:absolute rst:right-1/2 rst:top-4 rst:h-0.5 rst:w-full rst:-translate-y-1/2",n<=t?"rst:bg-primary-600 rst:dark:bg-primary-400 rst:forced-colors:forced-color-adjust-none rst:forced-colors:bg-[Highlight]":"rst:bg-[var(--roster-skeleton)] rst:forced-colors:forced-color-adjust-none rst:forced-colors:bg-[GrayText]"),"data-step-connector":n<=t?"filled":"empty"}),s.jsx("span",{"aria-hidden":"true",className:j.stepMarkerClass(a),"data-step-marker":"",children:a==="done"?s.jsx(y.StepCheck,{}):o.number(n)}),s.jsxs("span",{className:c.cn("rst:w-full rst:px-1 rst:text-xs rst:leading-4 rst:break-words",a==="current"?"rst:font-semibold rst:text-[var(--roster-control-text)]":"rst:text-gray-600 rst:dark:text-gray-400"),children:[p,a==="done"&&s.jsx("span",{className:"rst:sr-only",children:`, ${o.done}`})]})]},`${n}-${p}`)})}),s.jsx("span",{role:"status",className:"rst:sr-only","data-step-status":"",children:x})]})}exports.StepProgress=v;