@blakesteve/roster 5.17.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.
- package/README.md +122 -1
- package/dist/components/molecules/RadioTileGroup/RadioTileGroup.cjs +1 -0
- package/dist/components/molecules/RadioTileGroup/RadioTileGroup.d.ts +101 -0
- package/dist/components/molecules/RadioTileGroup/RadioTileGroup.js +142 -0
- package/dist/components/molecules/RadioTileGroup/radio-tile-group-variants.cjs +1 -0
- package/dist/components/molecules/RadioTileGroup/radio-tile-group-variants.d.ts +49 -0
- package/dist/components/molecules/RadioTileGroup/radio-tile-group-variants.js +34 -0
- package/dist/components/molecules/StepProgress/StepProgress.cjs +1 -1
- package/dist/components/molecules/StepProgress/StepProgress.js +29 -42
- package/dist/components/molecules/Timeline/Timeline.cjs +1 -0
- package/dist/components/molecules/Timeline/Timeline.d.ts +86 -0
- package/dist/components/molecules/Timeline/Timeline.js +217 -0
- package/dist/index.d.ts +3 -0
- package/dist/internal/step-check.cjs +1 -0
- package/dist/internal/step-check.d.ts +2 -0
- package/dist/internal/step-check.js +6 -0
- package/dist/internal/step-marker.cjs +1 -0
- package/dist/internal/step-marker.d.ts +10 -0
- package/dist/internal/step-marker.js +12 -0
- package/dist/meta.json +3 -3
- package/dist/roster.cjs +1 -1
- package/dist/roster.css +1 -1
- package/dist/roster.js +155 -147
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1328,6 +1328,12 @@ rather than a rewrite.
|
|
|
1328
1328
|
short scannable set that would otherwise spend three rows of a wide form. It
|
|
1329
1329
|
sets `aria-orientation` to match.
|
|
1330
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
|
+
|
|
1331
1337
|
Every radio is a 44x44 target, and the component is built so that it stays one:
|
|
1332
1338
|
each option's box is sized to contain its own target, which is why the options
|
|
1333
1339
|
can sit close together without a target ever reaching into its neighbor.
|
|
@@ -1340,6 +1346,68 @@ escape the box. Lay the options out across with `orientation` rather than
|
|
|
1340
1346
|
through `optionsClassName`, for the same reason: the horizontal spacing that
|
|
1341
1347
|
keeps two radios 44px apart rides on that prop.
|
|
1342
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
|
+
|
|
1343
1411
|
### Dialog
|
|
1344
1412
|
|
|
1345
1413
|
A modal dialog. Focus moves in when it opens, Tab stays inside, Escape and its
|
|
@@ -1732,7 +1800,8 @@ label and value on one baseline above the bar, an optional detail line below.
|
|
|
1732
1800
|
numbered marker and connectors that fill behind the current one;
|
|
1733
1801
|
`"segmented"` is the compact form, always with "Step 2 of 5". The current
|
|
1734
1802
|
step is `aria-current="step"`, finished ones say "done", and the position is
|
|
1735
|
-
said when it changes.
|
|
1803
|
+
said when it changes. For steps down the page with content of their own, see
|
|
1804
|
+
[Timeline](#timeline).
|
|
1736
1805
|
|
|
1737
1806
|
**LoadingDots.** Only for a model generating before its first token. It takes
|
|
1738
1807
|
the surrounding text's size and color, rests at 1, 0.6 and 0.3 opacity (also
|
|
@@ -1741,6 +1810,56 @@ shown beside it.
|
|
|
1741
1810
|
|
|
1742
1811
|
Every string in all five is a prop with an English default.
|
|
1743
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
|
+
|
|
1744
1863
|
### Menu
|
|
1745
1864
|
|
|
1746
1865
|
A button that opens a panel of actions: the menu button pattern. Compose it:
|
|
@@ -2431,8 +2550,10 @@ function App() {
|
|
|
2431
2550
|
| `SkeletonCard`, `SkeletonTableRow`, `SkeletonAvatar`, `SkeletonStat` | Skeleton presets in the real component's box |
|
|
2432
2551
|
| `ProgressField` | Progress with a label and value on one baseline and a detail line: the form to reach for |
|
|
2433
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)) |
|
|
2434
2554
|
| `Pullquote` | A line lifted out of prose, as `<figure>` + `<blockquote>` + `<figcaption>` |
|
|
2435
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)) |
|
|
2436
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)) |
|
|
2437
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)) |
|
|
2438
2559
|
| `Tabs` | Tab strip, plain (an underline) or liquid (a sliding pill); arrow keys, one tab stop, counts, 44px `lg` size (see [Tabs](#tabs)) |
|
|
@@ -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
|
|
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;
|