@maltjoy/mcp-server 0.4.0 → 0.6.0-beta.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/LICENSE +21 -0
- package/README.md +30 -3
- package/dist/doc/VJoyAdminBanner/guideline.md +284 -0
- package/dist/doc/VJoyAvailability/guideline.md +352 -0
- package/dist/doc/VJoyAvatar/guideline.md +192 -0
- package/dist/doc/VJoyAvatarsList/guideline.md +328 -0
- package/dist/doc/VJoyBadge/guideline.md +169 -0
- package/dist/doc/VJoyBadgeLevel/guideline.md +166 -0
- package/dist/doc/VJoyBlockSkeleton/guideline.md +150 -0
- package/dist/doc/VJoyBottomSheet/guideline.md +345 -0
- package/dist/doc/VJoyBottomSheetTrigger/guideline.md +156 -0
- package/dist/doc/VJoyButton/guideline.md +371 -0
- package/dist/doc/VJoyButton/metadata.json +0 -16
- package/dist/doc/VJoyButtonCard/guideline.md +384 -0
- package/dist/doc/VJoyCheckbox/guideline.md +317 -0
- package/dist/doc/VJoyCheckboxCard/guideline.md +381 -0
- package/dist/doc/VJoyCheckboxGroup/guideline.md +363 -0
- package/dist/doc/VJoyCheckboxGroup/metadata.json +7 -7
- package/dist/doc/VJoyCollapse/guideline.md +362 -0
- package/dist/doc/VJoyCollapseItem/guideline.md +394 -0
- package/dist/doc/VJoyCompanyAvatar/guideline.md +162 -0
- package/dist/doc/VJoyDialog/guideline.md +425 -0
- package/dist/doc/VJoyDialogTrigger/guideline.md +186 -0
- package/dist/doc/VJoyDot/guideline.md +177 -0
- package/dist/doc/VJoyDrawer/guideline.md +392 -0
- package/dist/doc/VJoyDrawerTrigger/guideline.md +145 -0
- package/dist/doc/VJoyDropdown/guideline.md +393 -0
- package/dist/doc/VJoyDropdown/metadata.json +6 -6
- package/dist/doc/VJoyDropdownList/guideline.md +396 -0
- package/dist/doc/VJoyDropdownList/metadata.json +7 -7
- package/dist/doc/VJoyDropzone/guideline.md +390 -0
- package/dist/doc/VJoyDropzone/metadata.json +36 -1
- package/dist/doc/VJoyFilterBar/guideline.md +419 -0
- package/dist/doc/VJoyFilterBarButton/guideline.md +359 -0
- package/dist/doc/VJoyFloatingActionsBar/guideline.md +372 -0
- package/dist/doc/VJoyFormError/guideline.md +325 -0
- package/dist/doc/VJoyFormError/metadata.json +0 -14
- package/dist/doc/VJoyFormFieldSkeleton/guideline.md +159 -0
- package/dist/doc/VJoyFormGroup/guideline.md +285 -0
- package/dist/doc/VJoyFunnel/guideline.md +349 -0
- package/dist/doc/VJoyFunnelFooter/guideline.md +178 -0
- package/dist/doc/VJoyFunnelHeader/guideline.md +204 -0
- package/dist/doc/VJoyHighlight/guideline.md +169 -0
- package/dist/doc/VJoyIcon/JoyIcon.types.ts +1 -1
- package/dist/doc/VJoyIcon/guideline.md +168 -0
- package/dist/doc/VJoyIcon/metadata.json +0 -1
- package/dist/doc/VJoyIconButton/guideline.md +169 -0
- package/dist/doc/VJoyIconButton/metadata.json +1 -1
- package/dist/doc/VJoyIndicator/guideline.md +168 -0
- package/dist/doc/VJoyIndicators/guideline.md +183 -0
- package/dist/doc/VJoyInput/JoyInput.types.ts +1 -6
- package/dist/doc/VJoyInput/guideline.md +401 -0
- package/dist/doc/VJoyInput/metadata.json +5 -14
- package/dist/doc/VJoyInputDigit/guideline.md +381 -0
- package/dist/doc/VJoyInputNumber/guideline.md +414 -0
- package/dist/doc/VJoyInputNumber/metadata.json +22 -17
- package/dist/doc/VJoyInputPassword/guideline.md +379 -0
- package/dist/doc/VJoyInputPassword/metadata.json +0 -14
- package/dist/doc/VJoyLabel/guideline.md +200 -0
- package/dist/doc/VJoyLink/guideline.md +360 -0
- package/dist/doc/VJoyListItem/guideline.md +335 -0
- package/dist/doc/VJoyMenu/guideline.md +304 -0
- package/dist/doc/VJoyMenu/metadata.json +21 -21
- package/dist/doc/VJoyMenuItem/guideline.md +352 -0
- package/dist/doc/VJoyPagination/guideline.md +355 -0
- package/dist/doc/VJoyPanel/guideline.md +238 -0
- package/dist/doc/VJoyPanelSection/guideline.md +160 -0
- package/dist/doc/VJoyPasswordRequirement/guideline.md +336 -0
- package/dist/doc/VJoyPasswordRequirement/metadata.json +1 -1
- package/dist/doc/VJoyProductTour/JoyProductTour.types.ts +0 -1
- package/dist/doc/VJoyProductTour/guideline.md +455 -0
- package/dist/doc/VJoyProductTour/metadata.json +1 -1
- package/dist/doc/VJoyProductTourTrigger/guideline.md +167 -0
- package/dist/doc/VJoyProgressBar/guideline.md +348 -0
- package/dist/doc/VJoyRadio/guideline.md +346 -0
- package/dist/doc/VJoyRadioGroup/guideline.md +331 -0
- package/dist/doc/VJoyRatingStars/guideline.md +306 -0
- package/dist/doc/VJoyScreenLoader/guideline.md +172 -0
- package/dist/doc/VJoySelect/guideline.md +396 -0
- package/dist/doc/VJoySelectableItem/guideline.md +351 -0
- package/dist/doc/VJoySelectableItemGroup/guideline.md +370 -0
- package/dist/doc/VJoySeparator/guideline.md +161 -0
- package/dist/doc/VJoySnackbar/guideline.md +402 -0
- package/dist/doc/VJoySpinner/guideline.md +168 -0
- package/dist/doc/VJoyStep/guideline.md +182 -0
- package/dist/doc/VJoyStepper/guideline.md +390 -0
- package/dist/doc/VJoyTab/guideline.md +298 -0
- package/dist/doc/VJoyTabs/guideline.md +353 -0
- package/dist/doc/VJoyTag/guideline.md +226 -0
- package/dist/doc/VJoyTagsInput/guideline.md +387 -0
- package/dist/doc/VJoyTagsList/guideline.md +311 -0
- package/dist/doc/VJoyTagsList/metadata.json +27 -27
- package/dist/doc/VJoyTemplate/guideline.md +366 -0
- package/dist/doc/VJoyText/guideline.md +175 -0
- package/dist/doc/VJoyText/metadata.json +1 -1
- package/dist/doc/VJoyTextarea/guideline.md +378 -0
- package/dist/doc/VJoyTitleBrand/guideline.md +155 -0
- package/dist/doc/VJoyToggle/guideline.md +327 -0
- package/dist/doc/VJoyTooltip/guideline.md +322 -0
- package/dist/doc/VJoyUserCard/guideline.md +268 -0
- package/dist/doc/VJoyUserCard/metadata.json +3 -17
- package/dist/doc/VJoyWalkthrough/guideline.md +351 -0
- package/dist/doc/VJoyWalkthroughTrigger/guideline.md +171 -0
- package/dist/doc/VJoyWrapper/guideline.md +173 -0
- package/dist/doc/css-classes.json +1 -15
- package/dist/doc/design-tokens.json +1 -0
- package/dist/doc/index.json +20 -30
- package/dist/index.js +22 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/get-component-guideline.d.ts +30 -0
- package/dist/tools/get-component-guideline.d.ts.map +1 -0
- package/dist/tools/get-component-guideline.js +51 -0
- package/dist/tools/get-component-guideline.js.map +1 -0
- package/dist/tools/get-documentation-page.d.ts +22 -0
- package/dist/tools/get-documentation-page.d.ts.map +1 -0
- package/dist/tools/get-documentation-page.js +21 -0
- package/dist/tools/get-documentation-page.js.map +1 -0
- package/dist/tools/list-documentation-pages.d.ts +22 -0
- package/dist/tools/list-documentation-pages.d.ts.map +1 -0
- package/dist/tools/list-documentation-pages.js +17 -0
- package/dist/tools/list-documentation-pages.js.map +1 -0
- package/dist/tools/search-documentation.d.ts +41 -0
- package/dist/tools/search-documentation.d.ts.map +1 -0
- package/dist/tools/search-documentation.js +25 -0
- package/dist/tools/search-documentation.js.map +1 -0
- package/dist/zeroheight-parser.d.ts +27 -0
- package/dist/zeroheight-parser.d.ts.map +1 -0
- package/dist/zeroheight-parser.js +77 -0
- package/dist/zeroheight-parser.js.map +1 -0
- package/package.json +10 -10
- package/dist/doc/VJoyMultiCheckbox/metadata.json +0 -332
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: VJoyProgressBar
|
|
3
|
+
status: draft
|
|
4
|
+
language: en
|
|
5
|
+
dsds: JoyProgressBar.dsds.json
|
|
6
|
+
dsdsVersion: "0.15.2"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Progress Bar
|
|
10
|
+
|
|
11
|
+
Use **`VJoyProgressBar`** when you need a **linear, determinate** progress indicator — for example showing how far a file upload has gone, how complete a profile is, or progress inside a walkthrough header.
|
|
12
|
+
|
|
13
|
+
It supports percentage-based or step-based values, optional numeric label, semantic color variants, and a smooth width transition. It is **read-only** (not interactive).
|
|
14
|
+
|
|
15
|
+
| | |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| **Code** | `VJoyProgressBar` from `@maltjoy/core-vue` |
|
|
18
|
+
| **Design** | [ZeroHeight — Progress Bar](https://zeroheight.com/6bf479565/) (page id `2271816`) |
|
|
19
|
+
| **Storybook** | [VJoyProgressBar docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-view-vjoyprogressbar--docs) |
|
|
20
|
+
| **DSDS (machine)** | [`JoyProgressBar.dsds.json`](./JoyProgressBar.dsds.json) |
|
|
21
|
+
| **Live API** | `joy_get_component_info("VJoyProgressBar")` |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## When to use this component
|
|
26
|
+
|
|
27
|
+
Use a progress bar when completion is **known and measurable** (0–100%) and a thin horizontal bar is enough context — upload progress, checklist completion, onboarding percentage, or a compact meter beside a title.
|
|
28
|
+
|
|
29
|
+
Prefer **`percentage`** when the backend or client already exposes a percent. Use **`steps`** + **`currentStep`** when progress is naturally expressed as “step 3 of 6” and you want the component to derive the fill width.
|
|
30
|
+
|
|
31
|
+
### When not to use this component
|
|
32
|
+
|
|
33
|
+
| Need | Use instead |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| Multi-step funnel with named steps and sub-steps | `VJoyStepper` + `VJoyStep` |
|
|
36
|
+
| Indeterminate / unknown duration loading | `VJoySpinner` or `VJoyScreenLoader` |
|
|
37
|
+
| Blocking full-page load | `VJoyScreenLoader` |
|
|
38
|
+
| Status message after an action | `pushVJoySnackbar` |
|
|
39
|
+
| Semantic status callout (success, warning, error copy) | `VJoyHighlight` |
|
|
40
|
+
| Custom bar with ad-hoc CSS | Still use `VJoyProgressBar` + Joy tokens — don’t reinvent chrome |
|
|
41
|
+
|
|
42
|
+
> **Product note:** Progress bars are lightly used in product UIs; for guided flows with step labels, design guidance favors **`VJoyStepper`**. Use `VJoyProgressBar` when a simple meter is intentional.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## How it works
|
|
47
|
+
|
|
48
|
+
### Anatomy
|
|
49
|
+
|
|
50
|
+
| Part | Required? | What it is |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| Root wrapper | Yes | `VJoyWrapper` (`nowrap`, `column-gap="1"`, `align="center"`) with color class `joy-progress-bar_{color}` |
|
|
53
|
+
| Track | Yes | `.joy-progress-bar-wrapper` — neutral background, full width, 4px height |
|
|
54
|
+
| Fill | Yes | `.joy-progress-bar-inner` — width driven by computed progress (`0%`–`100%`) |
|
|
55
|
+
| Label | No | `.joy-progress-bar-label` — pill showing `{percentage}%` when `showLabel` is true |
|
|
56
|
+
|
|
57
|
+
The fill animates width with `--joy-transition-duration-long` and `--joy-transition-timing-function`.
|
|
58
|
+
|
|
59
|
+
### Progress value
|
|
60
|
+
|
|
61
|
+
The component computes fill width in this order:
|
|
62
|
+
|
|
63
|
+
1. If **`percentage`** is set (including `0`), it wins — **`steps` / `currentStep` are ignored**.
|
|
64
|
+
2. Else if both **`steps`** and **`currentStep`** are set, width = `round((currentStep / steps) * 100)`.
|
|
65
|
+
3. Else width is **`0%`**.
|
|
66
|
+
|
|
67
|
+
Values are **clamped** to `0`–`100` (overflowing `currentStep` caps at 100%; negative values floor at 0%).
|
|
68
|
+
|
|
69
|
+
### Default values
|
|
70
|
+
|
|
71
|
+
- `color` defaults to **`secondary`**
|
|
72
|
+
- `showLabel` defaults to **`false`**
|
|
73
|
+
- `steps`, `currentStep`, and `percentage` are **optional** (unset → `0%` fill)
|
|
74
|
+
|
|
75
|
+
Props: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
|
|
76
|
+
|
|
77
|
+
### Label behavior
|
|
78
|
+
|
|
79
|
+
The built-in label renders **only** when **`showLabel` is true and `percentage` is defined** (including `0`). It does **not** appear for step-only mode (`steps` + `currentStep` without `percentage`). For step-based flows that need visible numbers, pass an explicit `percentage` or show copy outside the component.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Variants
|
|
84
|
+
|
|
85
|
+
`color` carries **semantic meaning**, not decoration. Pick the variant that matches the status being communicated.
|
|
86
|
+
|
|
87
|
+
Allowed values (from `TJoyProgressBarColors`): `primary`, `secondary`, `success`, `warning`, `error`.
|
|
88
|
+
|
|
89
|
+
### `secondary` (default)
|
|
90
|
+
|
|
91
|
+
Use for **neutral** progress — generic completion, onboarding percentage, or walkthrough progress where no success/warning/error signal is needed.
|
|
92
|
+
|
|
93
|
+
Avoid when the bar should read as a validation outcome (use `success`, `warning`, or `error`).
|
|
94
|
+
|
|
95
|
+
### `primary`
|
|
96
|
+
|
|
97
|
+
Use when the bar should align with the **brand primary** accent (marketing-scale or hero completion meters).
|
|
98
|
+
|
|
99
|
+
Avoid pairing several primary bars on one surface unless each tracks a distinct primary task.
|
|
100
|
+
|
|
101
|
+
### `success`
|
|
102
|
+
|
|
103
|
+
Use when progress reflects a **completed or on-track positive** outcome (e.g. profile 100% complete, upload finished).
|
|
104
|
+
|
|
105
|
+
Avoid for in-progress neutral tasks — `secondary` is clearer.
|
|
106
|
+
|
|
107
|
+
### `warning`
|
|
108
|
+
|
|
109
|
+
Use when progress is **incomplete or needs attention** but is not a hard failure (e.g. profile partially filled).
|
|
110
|
+
|
|
111
|
+
Avoid for successful completion states.
|
|
112
|
+
|
|
113
|
+
### `error`
|
|
114
|
+
|
|
115
|
+
Use when the meter communicates **failure or critical shortfall** (e.g. quota exceeded, validation threshold not met).
|
|
116
|
+
|
|
117
|
+
Avoid for ordinary in-progress loading.
|
|
118
|
+
|
|
119
|
+
### Not in the API: `info` / `neutral`
|
|
120
|
+
|
|
121
|
+
ZeroHeight and shared level tokens include **`info`** and **`neutral`**. They are **not** in `TJoyProgressBarColors`. Do not emit `color="info"` or `color="neutral"`. Use `secondary` for neutral meters or confirm with design before extending the API.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Sizes
|
|
126
|
+
|
|
127
|
+
`VJoyProgressBar` exposes **no `size` prop**. Visual scale is fixed inside the component.
|
|
128
|
+
|
|
129
|
+
| Aspect | Guidance |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| Track height | 4px — driven by component CSS; **don’t hardcode bar height** in apps |
|
|
132
|
+
| Track width | 100% of the wrapper; place inside a layout that defines available width (`VJoyWrapper`, panel body, etc.) |
|
|
133
|
+
| Label typography | `joy-font-primary-250` on the pill — don’t override font size in product CSS |
|
|
134
|
+
| Density | For funnels with many named steps, prefer `VJoyStepper` (bar width adapts to step count there) — not this component |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## States
|
|
139
|
+
|
|
140
|
+
| State | What the user sees |
|
|
141
|
+
| --- | --- |
|
|
142
|
+
| Empty (`0%`) | Track only; fill width `0%`; label `0%` when `showLabel` + `percentage` |
|
|
143
|
+
| In progress (`1%`–`99%`) | Partial fill; optional label |
|
|
144
|
+
| Complete (`100%`) | Full fill; optional label `100%` |
|
|
145
|
+
| Step-based | Fill derived from `currentStep` / `steps`; no built-in label unless you also pass `percentage` |
|
|
146
|
+
| Color emphasis | Fill and label pill tint follow `color` token mapping |
|
|
147
|
+
|
|
148
|
+
There is no hover, focus, disabled, or loading prop — the bar is presentational. Drive updates by changing `percentage` or `currentStep` reactively.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Best practices
|
|
153
|
+
|
|
154
|
+
Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
|
|
155
|
+
|
|
156
|
+
### When to show progress
|
|
157
|
+
|
|
158
|
+
- **SHOULD** use `VJoyProgressBar` only when progress is **determinate** (you can compute 0–100%).
|
|
159
|
+
- **SHOULD** prefer `VJoyStepper` when users need **step names** and funnel navigation context.
|
|
160
|
+
- **SHOULD** pair the bar with a **visible heading or status** so meaning isn’t color-only.
|
|
161
|
+
- **MUST NOT** use a progress bar for **indeterminate** waits — use `VJoySpinner`.
|
|
162
|
+
|
|
163
|
+
### Implementation
|
|
164
|
+
|
|
165
|
+
- **MUST** use only `color` values from MCP or `JoyProgressBar.types.ts`.
|
|
166
|
+
- **MUST** pass **`percentage`** when you need the built-in numeric label (`showLabel`).
|
|
167
|
+
- **MUST** clamp or validate upstream values; the component clamps fill width but labels echo the `percentage` prop as given.
|
|
168
|
+
- **MUST** style with Joy tokens — no custom `.joy-progress-bar-*` chrome (height, radius, colors).
|
|
169
|
+
- **MUST NOT** invent props (`variant`, `size`, `value`, `max`, …).
|
|
170
|
+
- **SHOULD** prefer `percentage` when the API already returns a percent; use `steps` / `currentStep` only when step indices are the source of truth.
|
|
171
|
+
- **SHOULD** update `percentage` or `currentStep` as work advances — avoid jumping backwards without user context.
|
|
172
|
+
|
|
173
|
+
### Do / don’t
|
|
174
|
+
|
|
175
|
+
| Do | Don’t |
|
|
176
|
+
| --- | --- |
|
|
177
|
+
| `color="secondary"` for neutral meters | Random hex fills on a custom `<div>` |
|
|
178
|
+
| `showLabel` + `percentage` for numeric feedback | `showLabel` alone in step-only mode (label won’t render) |
|
|
179
|
+
| `VJoyStepper` for named funnel steps | Progress bar as the only step indicator in a wizard |
|
|
180
|
+
| `VJoySpinner` for unknown duration | Animated bar with fake percentage |
|
|
181
|
+
| Heading + bar for context | Color-only status with no text |
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Content
|
|
186
|
+
|
|
187
|
+
The optional label shows a **percentage only** (`42%`). It does not accept custom slot text.
|
|
188
|
+
|
|
189
|
+
### Label formula
|
|
190
|
+
|
|
191
|
+
**Integer percentage + `%` symbol** — rendered by the component from the `percentage` prop:
|
|
192
|
+
|
|
193
|
+
- ✅ `0%`, `45%`, `100%`
|
|
194
|
+
- ❌ Custom strings (`3 of 6`, `Almost done`) — use adjacent `VJoyText` or stepper labels instead
|
|
195
|
+
|
|
196
|
+
### Writing rules
|
|
197
|
+
|
|
198
|
+
| Strength | Rule |
|
|
199
|
+
| --- | --- |
|
|
200
|
+
| **MUST** | Provide a **visible title or status** near the bar when the task name isn’t obvious from context |
|
|
201
|
+
| **MUST NOT** | Rely on color alone to communicate success, warning, or error |
|
|
202
|
+
| **SHOULD** | Enable `showLabel` when exact percent helps (uploads, profile completion) |
|
|
203
|
+
| **SHOULD** | Hide `showLabel` when the bar is decorative or redundant with nearby copy |
|
|
204
|
+
| **SHOULD** | Use `success` / `warning` / `error` colors consistently with `VJoyHighlight` semantics on the same screen |
|
|
205
|
+
| **SHOULD NOT** | Show both a stepper and a redundant percentage label for the same funnel step |
|
|
206
|
+
|
|
207
|
+
### Patterns
|
|
208
|
+
|
|
209
|
+
**Uploads** — Bind `percentage` to upload progress; `showLabel` optional; keep filename/status in adjacent text.
|
|
210
|
+
|
|
211
|
+
**Profile / checklist completion** — `percentage` from completed fields; `color="success"` at 100%; `secondary` while in progress.
|
|
212
|
+
|
|
213
|
+
**Walkthrough header** — Slot pattern in `VJoyWalkthrough` (`walkthrough-progress`): compact bar + label; subtitle can carry human-readable copy (`80% completed`).
|
|
214
|
+
|
|
215
|
+
**Step-derived width** — Use `steps` + `currentStep` when you only need the fill (no built-in label); add external “Step 2 of 5” copy if users need numbers.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Accessibility
|
|
220
|
+
|
|
221
|
+
Behaviors (what must happen), not slogans.
|
|
222
|
+
|
|
223
|
+
| Behavior | Expectation |
|
|
224
|
+
| --- | --- |
|
|
225
|
+
| Role | Component uses `<div>` elements — **no `role="progressbar"`** is set by default |
|
|
226
|
+
| Name | **MUST** expose purpose in nearby visible text (title, status, walkthrough subtitle) — the bar alone is not a sufficient name |
|
|
227
|
+
| Value | When `showLabel` + `percentage` are set, the numeric value is visible; otherwise provide equivalent text in context |
|
|
228
|
+
| Motion | Width animates on change — respect `prefers-reduced-motion` at app level if you add global overrides |
|
|
229
|
+
| Color | Don’t use `color` as the only indicator of success, warning, or error — pair with text or icons |
|
|
230
|
+
| Keyboard | Not focusable — no keyboard interaction required |
|
|
231
|
+
|
|
232
|
+
**SHOULD** wrap or supplement with an accessible name when progress is critical (e.g. `aria-label="Upload progress"` on a parent region, or visible heading associated via `aria-labelledby`). If native `role="progressbar"` with `aria-valuenow` / `aria-valuemin` / `aria-valuemax` is required, confirm with design whether to extend the component — don’t fake it with clickable divs.
|
|
233
|
+
|
|
234
|
+
Target: **WCAG 2.2 AA** where the product requires it.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Examples
|
|
239
|
+
|
|
240
|
+
```vue
|
|
241
|
+
<script setup lang="ts">
|
|
242
|
+
import { VJoyProgressBar } from '@maltjoy/core-vue'
|
|
243
|
+
</script>
|
|
244
|
+
|
|
245
|
+
<template>
|
|
246
|
+
<!-- Percentage with label (default color) -->
|
|
247
|
+
<VJoyProgressBar :percentage="45" show-label />
|
|
248
|
+
|
|
249
|
+
<!-- Step-derived width (no built-in label) -->
|
|
250
|
+
<VJoyProgressBar :steps="6" :current-step="3" />
|
|
251
|
+
|
|
252
|
+
<!-- Semantic colors -->
|
|
253
|
+
<VJoyProgressBar :percentage="100" color="success" show-label />
|
|
254
|
+
<VJoyProgressBar :percentage="40" color="warning" />
|
|
255
|
+
<VJoyProgressBar :percentage="15" color="error" show-label />
|
|
256
|
+
|
|
257
|
+
<!-- Primary accent -->
|
|
258
|
+
<VJoyProgressBar :percentage="80" color="primary" />
|
|
259
|
+
|
|
260
|
+
<!-- Zero percent is valid (v6+) -->
|
|
261
|
+
<VJoyProgressBar :percentage="0" show-label />
|
|
262
|
+
|
|
263
|
+
<!-- Label requires percentage — not steps alone -->
|
|
264
|
+
<VJoyProgressBar :percentage="50" :steps="4" :current-step="1" show-label />
|
|
265
|
+
</template>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
In Nuxt (or apps with auto-import), omit the import if `VJoyProgressBar` is already global.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Related
|
|
273
|
+
|
|
274
|
+
| Component | Use when |
|
|
275
|
+
| --- | --- |
|
|
276
|
+
| `VJoyStepper` / `VJoyStep` | Named multi-step funnel with step labels |
|
|
277
|
+
| `VJoySpinner` | Indeterminate loading (button, inline) |
|
|
278
|
+
| `VJoyScreenLoader` | Full-viewport blocking load |
|
|
279
|
+
| `VJoyWalkthrough` | Onboarding overlay — embeds progress via `walkthrough-progress` slot |
|
|
280
|
+
| `VJoyHighlight` | Persistent success / warning / error messaging with copy |
|
|
281
|
+
| `VJoyWrapper` | Layout wrapper used internally; also for grouping bar + adjacent text |
|
|
282
|
+
| `VJoyText` | Human-readable status beside the bar |
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## For agents
|
|
287
|
+
|
|
288
|
+
Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoyProgressBar.dsds.json`](./JoyProgressBar.dsds.json).
|
|
289
|
+
|
|
290
|
+
### Pick the right component
|
|
291
|
+
|
|
292
|
+
| If the intent is… | Emit… | Do not emit… |
|
|
293
|
+
| --- | --- | --- |
|
|
294
|
+
| Known % completion, thin meter | `VJoyProgressBar` | Custom `<div>` with inline width % |
|
|
295
|
+
| Named steps in a funnel | `VJoyStepper` | `VJoyProgressBar` as the only step UI |
|
|
296
|
+
| Unknown wait time | `VJoySpinner` / `VJoyScreenLoader` | `VJoyProgressBar` with fake oscillating % |
|
|
297
|
+
| Toast after save | `pushVJoySnackbar` | Progress bar for transient feedback |
|
|
298
|
+
| Walkthrough % in header | `VJoyProgressBar` in `walkthrough-progress` | Duplicate bar + random copy |
|
|
299
|
+
|
|
300
|
+
### Hard rules
|
|
301
|
+
|
|
302
|
+
1. **MUST** call `joy_get_component_info("VJoyProgressBar")` (or read `JoyProgressBar.types.ts` / `VJoyProgressBar.vue`) before generating props.
|
|
303
|
+
2. **MUST** use only documented props: `color`, `steps`, `currentStep`, `percentage`, `showLabel`.
|
|
304
|
+
3. **MUST** pass **`percentage`** when `showLabel` is true — the label does not render for step-only mode.
|
|
305
|
+
4. **MUST NOT** invent props (`variant`, `size`, `value`, `max`, `label`, …).
|
|
306
|
+
5. **MUST NOT** emit `color="info"` or `color="neutral"`.
|
|
307
|
+
6. **MUST NOT** override `.joy-progress-bar-wrapper` / `-inner` height, radius, or colors in app CSS.
|
|
308
|
+
7. **SHOULD** default to `color="secondary"`; use semantic colors only when status matches.
|
|
309
|
+
8. **SHOULD** prefer `percentage` over `steps`/`currentStep` when both are available — `percentage` overrides step math.
|
|
310
|
+
|
|
311
|
+
### Valid enums (from `JoyProgressBar.types.ts`)
|
|
312
|
+
|
|
313
|
+
| Prop | Values |
|
|
314
|
+
| --- | --- |
|
|
315
|
+
| `color` | `primary`, `secondary`, `success`, `warning`, `error` |
|
|
316
|
+
| `showLabel` | `true`, `false` |
|
|
317
|
+
| `percentage` | `number` (0–100; component clamps fill width) |
|
|
318
|
+
| `steps` / `currentStep` | `number` (positive integers expected) |
|
|
319
|
+
|
|
320
|
+
### Checklist
|
|
321
|
+
|
|
322
|
+
- [ ] Correct component (`VJoyProgressBar` or named alternative)
|
|
323
|
+
- [ ] Props / enums from MCP or types only
|
|
324
|
+
- [ ] `percentage` set when `showLabel` is needed
|
|
325
|
+
- [ ] No invented props / no custom progress-bar chrome CSS
|
|
326
|
+
- [ ] Determinate progress only; spinner for indeterminate
|
|
327
|
+
- [ ] Context text or heading — not color-only status
|
|
328
|
+
- [ ] `VJoyStepper` used when step names matter
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## DSDS mapping
|
|
333
|
+
|
|
334
|
+
This Markdown mirrors [`JoyProgressBar.dsds.json`](./JoyProgressBar.dsds.json) (DSDS **0.15.2**).
|
|
335
|
+
|
|
336
|
+
| Section here | DSDS block |
|
|
337
|
+
| --- | --- |
|
|
338
|
+
| When to use / not | `use-cases` |
|
|
339
|
+
| Anatomy / progress logic | `anatomy` |
|
|
340
|
+
| Colors | `variants` |
|
|
341
|
+
| Single-size spec | `design-specifications` |
|
|
342
|
+
| States | `states` |
|
|
343
|
+
| Best practices / content rules | `guidelines` (+ `content`) |
|
|
344
|
+
| Accessibility | `accessibility` |
|
|
345
|
+
| Examples | `sections` |
|
|
346
|
+
| For agents | `agentDocumentBlocks` |
|
|
347
|
+
| Props detail | `api` in JSON + Joy MCP |
|
|
348
|
+
| Imports | `imports` in JSON |
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: VJoyRadio
|
|
3
|
+
status: draft
|
|
4
|
+
language: en
|
|
5
|
+
dsds: JoyRadio.dsds.json
|
|
6
|
+
dsdsVersion: "0.15.2"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Radio
|
|
10
|
+
|
|
11
|
+
Use **`VJoyRadio`** when the user must pick **exactly one** option from a small, visible set of mutually exclusive choices.
|
|
12
|
+
|
|
13
|
+
Each radio is a labeled option with optional supporting copy and content slots. Pair radios inside **`VJoyRadioGroup`** so the group gets a fieldset, legend, layout, and shared invalid state.
|
|
14
|
+
|
|
15
|
+
| | |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| **Code** | `VJoyRadio` from `@maltjoy/core-vue` |
|
|
18
|
+
| **Design** | [Figma — Joy components](https://www.figma.com/design/jfndp2QBJGS9yeuWbeGuvD/) |
|
|
19
|
+
| **Storybook** | [VJoyRadioGroup docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-forms-vjoyradiogroup--docs) (includes `VJoyRadio` examples) |
|
|
20
|
+
| **DSDS (machine)** | [`JoyRadio.dsds.json`](./JoyRadio.dsds.json) |
|
|
21
|
+
| **Live API** | `joy_get_component_info("VJoyRadio")` |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## When to use this component
|
|
26
|
+
|
|
27
|
+
Use a radio when only one answer is valid and every option should stay visible — for example choosing a billing cycle, a delivery method, or a profile type.
|
|
28
|
+
|
|
29
|
+
Radios work best with **2–7** parallel options. When options need extra description, pricing, or conditional fields, use the `outline` theme and the content slots rather than stacking unrelated UI beside a bare label.
|
|
30
|
+
|
|
31
|
+
### When not to use this component
|
|
32
|
+
|
|
33
|
+
| Need | Use instead |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| Multiple selections from a list | `VJoyCheckbox` inside `VJoyCheckboxGroup` |
|
|
36
|
+
| Card-style multi-select | `VJoyCheckboxCard` |
|
|
37
|
+
| A single on/off preference | `VJoyToggle` (or a checkbox when the label is a statement) |
|
|
38
|
+
| Many options (roughly 8+) or searchable lists | `VJoySelect` |
|
|
39
|
+
| Group label, layout, and shared validation | `VJoyRadioGroup` — **always** wrap radios; don’t use `VJoyFormGroup` around a lone radio |
|
|
40
|
+
| Custom-styled native `<input type="radio">` | Still use `VJoyRadio` + Joy tokens — don’t reinvent chrome |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## How it works
|
|
45
|
+
|
|
46
|
+
### Anatomy
|
|
47
|
+
|
|
48
|
+
| Part | Required? | What it is |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| Native radio input | Yes | Visually hidden `<input type="radio">` with `data-joy-form-field` |
|
|
51
|
+
| Label (`default` slot) | Yes | Primary option text beside the radio circle |
|
|
52
|
+
| Radio circle | Yes | Styled via the label pseudo-element (not a separate component) |
|
|
53
|
+
| Sublabel (`radio-sublabel` slot) | No | Secondary, pre-styled helper text under the label |
|
|
54
|
+
| Header right (`radio-header-right` slot) | No | Short metadata in the top-right (e.g. a tag) — non-interactive only |
|
|
55
|
+
| Content (`radio-content` slot) | No | Always-visible body content below the label (common with `outline`) |
|
|
56
|
+
| Expandable content (`expandable-content` slot) | No | Revealed only when this radio is selected (`outline` theme) |
|
|
57
|
+
| Focus ring | Yes | Theme-specific focus treatment — don’t remove it in product CSS |
|
|
58
|
+
|
|
59
|
+
### Default values
|
|
60
|
+
|
|
61
|
+
- `theme` defaults to **`default`**
|
|
62
|
+
- `disabled` defaults to **`false`**
|
|
63
|
+
- `required` defaults to **`false`**
|
|
64
|
+
- `invalid` defaults to **`undefined`** (inherits from parent `VJoyRadioGroup` when omitted)
|
|
65
|
+
- `value` is **required** — the option’s submitted value
|
|
66
|
+
|
|
67
|
+
Props, events, and slots: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
|
|
68
|
+
|
|
69
|
+
### Grouping and model
|
|
70
|
+
|
|
71
|
+
- **MUST** place every `VJoyRadio` inside `VJoyRadioGroup`.
|
|
72
|
+
- **MUST** bind the **same** `v-model` (`modelValue`) on each sibling radio.
|
|
73
|
+
- **MUST** give siblings the same `name` so the browser treats them as one group.
|
|
74
|
+
- **MUST** use a distinct `value` per option.
|
|
75
|
+
- Set group-level `invalid` on `VJoyRadioGroup`; override per radio with `invalid` only when needed.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Variants
|
|
80
|
+
|
|
81
|
+
`theme` controls layout density and which slots are meaningful — not a color swatch choice.
|
|
82
|
+
|
|
83
|
+
### `default`
|
|
84
|
+
|
|
85
|
+
Use for compact, inline option lists (horizontal or vertical groups) where the label alone is enough.
|
|
86
|
+
|
|
87
|
+
Avoid when each option needs a bordered card, always-on body content, or fields that appear only after selection.
|
|
88
|
+
|
|
89
|
+
### `outline`
|
|
90
|
+
|
|
91
|
+
Use when options should read as **selectable cards** — pricing tiers, plan comparison, or choices with sublabels and extra content.
|
|
92
|
+
|
|
93
|
+
Supports `radio-sublabel`, `radio-content`, `radio-header-right`, and `expandable-content`. The expandable region opens with a height/opacity transition when the radio becomes checked.
|
|
94
|
+
|
|
95
|
+
Avoid in dense horizontal toolbars; Storybook documents `outline` for **vertical** group layouts.
|
|
96
|
+
|
|
97
|
+
### Not in the API: size
|
|
98
|
+
|
|
99
|
+
There is no `size` prop. The radio control uses an internal `--radio-size` token (20px). Don’t hardcode circle dimensions in apps.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Sizes
|
|
104
|
+
|
|
105
|
+
`VJoyRadio` exposes **no size API**. Visual scale comes from Joy form tokens inside the component.
|
|
106
|
+
|
|
107
|
+
| Aspect | Guidance |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| Radio circle | Fixed via `--radio-size` (20px) and Joy color tokens — don’t override in product CSS |
|
|
110
|
+
| Label typography | Inherited from Joy primary body styles |
|
|
111
|
+
| Outline cards | Padding and border from Joy spacing / form tokens (`--joy-core-spacing-4`, `--joy-form-border-width`) |
|
|
112
|
+
| Density | Prefer `VJoyRadioGroup` `direction` and slot content for density — not invented `size` props |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## States
|
|
117
|
+
|
|
118
|
+
| State | What the user sees / can do |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| Default (unchecked) | Empty radio circle; option is selectable |
|
|
121
|
+
| Checked | Filled circle; label weight increases; `outline` border/elevation highlight |
|
|
122
|
+
| Hover | Circle border uses hover token (pointer devices); `outline` border accent on hover when enabled |
|
|
123
|
+
| Focus | Visible focus ring — `default` theme on the circle; `outline` theme on the card |
|
|
124
|
+
| Disabled | Muted label and circle; `not-allowed` cursor; **disabled styling wins over invalid** |
|
|
125
|
+
| Invalid | Error color on circle and/or `outline` border; inherits from `VJoyRadioGroup` unless overridden per radio |
|
|
126
|
+
| Expanded (`outline` + `expandable-content`) | Slot content visible only while this radio is checked |
|
|
127
|
+
|
|
128
|
+
When validation fails, set `invalid` on the **group** and show a `VJoyFormError` (or equivalent) explaining what to fix. Don’t rely on color alone.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Best practices
|
|
133
|
+
|
|
134
|
+
Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
|
|
135
|
+
|
|
136
|
+
### Selection and hierarchy
|
|
137
|
+
|
|
138
|
+
- **SHOULD** offer **2–7** options; beyond that, prefer `VJoySelect`.
|
|
139
|
+
- **SHOULD** pre-select a sensible default when one option is clearly recommended.
|
|
140
|
+
- **SHOULD** keep option labels **parallel** (same grammatical structure and length order of magnitude).
|
|
141
|
+
- **MUST NOT** use radios when the user may need “none of the above” unless product rules allow clearing the group.
|
|
142
|
+
- **MUST NOT** wrap `VJoyRadio` in `VJoyFormGroup` — use `VJoyRadioGroup` (enforced by ESLint).
|
|
143
|
+
|
|
144
|
+
### Implementation
|
|
145
|
+
|
|
146
|
+
- **MUST** use only documented props and `theme` values from MCP or `JoyRadio.types.ts` (`default`, `outline`).
|
|
147
|
+
- **MUST** share one `v-model` and one `name` across siblings in the same group.
|
|
148
|
+
- **MUST** style with Joy tokens — no custom `.joy-radio` chrome overrides.
|
|
149
|
+
- **MUST NOT** invent props (`size`, `variant`, `color`, …).
|
|
150
|
+
- **MUST NOT** put interactive controls (buttons, links) in `radio-header-right` — display-only metadata only.
|
|
151
|
+
|
|
152
|
+
### Interaction
|
|
153
|
+
|
|
154
|
+
- **MUST** set `disabled` on individual radios the user cannot choose (not just visually dim the label).
|
|
155
|
+
- **SHOULD** explain **why** an option is disabled when the reason isn’t obvious from context.
|
|
156
|
+
- **SHOULD** use `expandable-content` for fields that belong to the selected option — not for unrelated UI.
|
|
157
|
+
|
|
158
|
+
### Do / don’t
|
|
159
|
+
|
|
160
|
+
| Do | Don’t |
|
|
161
|
+
| --- | --- |
|
|
162
|
+
| `VJoyRadioGroup` + shared `v-model` | Lone radios without a group |
|
|
163
|
+
| Parallel, scannable labels | Long paragraphs as the primary label |
|
|
164
|
+
| `outline` + slots for rich options | Custom card divs that behave like radios |
|
|
165
|
+
| Group `invalid` + error message | Error color with no explanation |
|
|
166
|
+
| `radio-header-right` for tags / short meta | Buttons or links in the header-right slot |
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Content
|
|
171
|
+
|
|
172
|
+
Radio copy should make each option distinct at a glance and read consistently as a set.
|
|
173
|
+
|
|
174
|
+
### Label formula
|
|
175
|
+
|
|
176
|
+
**Short noun phrase or concise statement** naming the option:
|
|
177
|
+
|
|
178
|
+
- ✅ Monthly billing
|
|
179
|
+
- ✅ Standard delivery
|
|
180
|
+
- ✅ Freelancer
|
|
181
|
+
|
|
182
|
+
Use the **same part of speech** across siblings (all nouns, or all short sentences).
|
|
183
|
+
|
|
184
|
+
### Sublabel and body copy
|
|
185
|
+
|
|
186
|
+
- **`radio-sublabel`** — one line of supporting detail (price, timing, eligibility).
|
|
187
|
+
- **`radio-content`** — always-visible elaboration (feature list, highlight).
|
|
188
|
+
- **`expandable-content`** — inputs or details that only apply after selection.
|
|
189
|
+
|
|
190
|
+
### Writing rules
|
|
191
|
+
|
|
192
|
+
| Strength | Rule |
|
|
193
|
+
| --- | --- |
|
|
194
|
+
| **MUST** | Label the **option**, not the action — ✅ `Express shipping` · ❌ `Select express shipping` |
|
|
195
|
+
| **MUST** | Keep primary labels ≤ ~40 characters; move detail to sublabel or content slots |
|
|
196
|
+
| **MUST** | Use sentence case for labels and sublabels |
|
|
197
|
+
| **MUST NOT** | Repeat the group legend text in every option label |
|
|
198
|
+
| **MUST NOT** | Use yes/no as two radio labels when the question isn’t literally binary — name the outcomes |
|
|
199
|
+
| **SHOULD** | Put numbers, prices, or badges in `radio-header-right` or sublabel, not the main label |
|
|
200
|
+
| **SHOULD** | Write the group legend as a question or instruction (`Billing frequency`, `How do you work?`) via `VJoyRadioGroup`’s `radio-group-legend` slot with `VJoyLabel` |
|
|
201
|
+
| **SHOULD** | Localize all slot content; `value` stays a stable programmatic key |
|
|
202
|
+
|
|
203
|
+
### Patterns
|
|
204
|
+
|
|
205
|
+
**Plan / tier pickers** — `outline` theme: title in `default`, price or cadence in `radio-sublabel`, feature summary in `radio-content`, payment fields in `expandable-content` when selected.
|
|
206
|
+
|
|
207
|
+
**Settings with side effects** — sublabel states what changes if the user picks this option (`Invoices are sent on the 1st of each month`).
|
|
208
|
+
|
|
209
|
+
**Validation** — group legend + `VJoyFormError` below the group: ✅ `Choose a delivery method` + `Select one option to continue`.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Accessibility
|
|
214
|
+
|
|
215
|
+
Behaviors (what must happen), not slogans.
|
|
216
|
+
|
|
217
|
+
| Behavior | Expectation |
|
|
218
|
+
| --- | --- |
|
|
219
|
+
| Role | Native `<input type="radio">` inside a `<label>`; group wrapped in `VJoyRadioGroup` `<fieldset role="radiogroup">` |
|
|
220
|
+
| Name | Option name from the `default` slot (associated via `<label>`); group name from `radio-group-legend` |
|
|
221
|
+
| Keyboard | **Arrow keys** move between radios in the same group (browser default); **Space** selects focused radio |
|
|
222
|
+
| Focus | Visible focus ring — `joy-radio--focus` / `joy-radio--outline-focus`; never `outline: none` on product overrides |
|
|
223
|
+
| Disabled | `disabled` on the input; don’t rely on color alone — explain when needed |
|
|
224
|
+
| Invalid | Error styling plus programmatic invalid state from group; pair with visible error text |
|
|
225
|
+
| Expansion | Expanding content must not trap focus; treat revealed fields as part of the selected option’s context |
|
|
226
|
+
|
|
227
|
+
Target: **WCAG 2.2 AA** where the product requires it.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Examples
|
|
232
|
+
|
|
233
|
+
```vue
|
|
234
|
+
<script setup lang="ts">
|
|
235
|
+
import { ref } from 'vue'
|
|
236
|
+
import { VJoyRadio, VJoyRadioGroup, VJoyLabel } from '@maltjoy/core-vue'
|
|
237
|
+
|
|
238
|
+
const billing = ref('monthly')
|
|
239
|
+
</script>
|
|
240
|
+
|
|
241
|
+
<template>
|
|
242
|
+
<!-- Default theme — compact list -->
|
|
243
|
+
<VJoyRadioGroup>
|
|
244
|
+
<template #radio-group-legend>
|
|
245
|
+
<VJoyLabel>Billing frequency</VJoyLabel>
|
|
246
|
+
</template>
|
|
247
|
+
|
|
248
|
+
<VJoyRadio v-model="billing" name="billing" value="monthly">Monthly</VJoyRadio>
|
|
249
|
+
<VJoyRadio v-model="billing" name="billing" value="yearly">Yearly</VJoyRadio>
|
|
250
|
+
</VJoyRadioGroup>
|
|
251
|
+
|
|
252
|
+
<!-- Outline theme — rich option -->
|
|
253
|
+
<VJoyRadioGroup direction="vertical">
|
|
254
|
+
<template #radio-group-legend>
|
|
255
|
+
<VJoyLabel>Delivery method</VJoyLabel>
|
|
256
|
+
</template>
|
|
257
|
+
|
|
258
|
+
<VJoyRadio v-model="billing" theme="outline" name="delivery" value="standard">
|
|
259
|
+
Standard delivery
|
|
260
|
+
<template #radio-sublabel>3–5 business days</template>
|
|
261
|
+
<template #expandable-content>
|
|
262
|
+
<!-- Fields shown only when this option is selected -->
|
|
263
|
+
</template>
|
|
264
|
+
</VJoyRadio>
|
|
265
|
+
</VJoyRadioGroup>
|
|
266
|
+
|
|
267
|
+
<!-- Disabled option -->
|
|
268
|
+
<VJoyRadio v-model="billing" name="billing" value="enterprise" disabled>
|
|
269
|
+
Enterprise (contact sales)
|
|
270
|
+
</VJoyRadio>
|
|
271
|
+
</template>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
In Nuxt (or apps with auto-import), omit the import if `VJoyRadio` is already global.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Related
|
|
279
|
+
|
|
280
|
+
| Component | Use when |
|
|
281
|
+
| --- | --- |
|
|
282
|
+
| `VJoyRadioGroup` | Grouping, legend, layout (`direction`), and shared `invalid` |
|
|
283
|
+
| `VJoyLabel` | Accessible group legend in `radio-group-legend` |
|
|
284
|
+
| `VJoyFormError` | Explaining validation failures for the group |
|
|
285
|
+
| `VJoyCheckbox` / `VJoyCheckboxGroup` | Multiple selections |
|
|
286
|
+
| `VJoySelect` | Long or searchable option lists |
|
|
287
|
+
| `VJoyTag` | Short metadata in `radio-header-right` |
|
|
288
|
+
| `VJoyHighlight` | Informational blocks in `radio-content` or `expandable-content` |
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## For agents
|
|
293
|
+
|
|
294
|
+
Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoyRadio.dsds.json`](./JoyRadio.dsds.json).
|
|
295
|
+
|
|
296
|
+
### Pick the right component
|
|
297
|
+
|
|
298
|
+
| If the intent is… | Emit… | Do not emit… |
|
|
299
|
+
| --- | --- | --- |
|
|
300
|
+
| Exactly one of a few visible options | `VJoyRadio` inside `VJoyRadioGroup` | Standalone radio or `VJoyFormGroup` wrapper |
|
|
301
|
+
| Multiple selections | `VJoyCheckbox` | `VJoyRadio` with duplicate values |
|
|
302
|
+
| Long dropdown list | `VJoySelect` | Many `VJoyRadio` rows |
|
|
303
|
+
| Rich card option with conditional fields | `VJoyRadio` `theme="outline"` + slots | Custom clickable `<div>` cards |
|
|
304
|
+
| Group validation error | `invalid` on `VJoyRadioGroup` + error text | `invalid` on every radio without a message |
|
|
305
|
+
|
|
306
|
+
### Hard rules
|
|
307
|
+
|
|
308
|
+
1. **MUST** call `joy_get_component_info("VJoyRadio")` and `joy_get_component_types("VJoyRadio")` (or read types) before generating props.
|
|
309
|
+
2. **MUST** wrap all `VJoyRadio` instances in `VJoyRadioGroup`.
|
|
310
|
+
3. **MUST** bind the same `v-model` and `name` to every sibling in a group; each `value` must be unique.
|
|
311
|
+
4. **MUST** pass a required `value` prop on every radio.
|
|
312
|
+
5. **MUST NOT** invent props (`size`, `variant`, `color`, …) or custom `.joy-radio` CSS.
|
|
313
|
+
6. **MUST NOT** use interactive elements in `radio-header-right`.
|
|
314
|
+
7. **SHOULD** use `theme="outline"` only with `direction="vertical"` on the group.
|
|
315
|
+
8. **SHOULD** use `expandable-content` only with `theme="outline"` and only for content tied to the selected option.
|
|
316
|
+
|
|
317
|
+
### Checklist
|
|
318
|
+
|
|
319
|
+
- [ ] `VJoyRadio` inside `VJoyRadioGroup` (not `VJoyFormGroup`)
|
|
320
|
+
- [ ] Shared `v-model` + `name`; unique `value` per option
|
|
321
|
+
- [ ] `theme` is `default` or `outline` only (from types)
|
|
322
|
+
- [ ] No invented props / no custom radio chrome CSS
|
|
323
|
+
- [ ] Group legend via `radio-group-legend` + `VJoyLabel`
|
|
324
|
+
- [ ] Validation: `invalid` on group + visible error message
|
|
325
|
+
- [ ] Parallel, concise labels; detail in sublabel/content slots
|
|
326
|
+
- [ ] `radio-header-right` is display-only (e.g. `VJoyTag`)
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## DSDS mapping
|
|
331
|
+
|
|
332
|
+
This Markdown mirrors [`JoyRadio.dsds.json`](./JoyRadio.dsds.json) (DSDS **0.15.2**).
|
|
333
|
+
|
|
334
|
+
| Section here | DSDS block |
|
|
335
|
+
| --- | --- |
|
|
336
|
+
| When to use / not | `use-cases` |
|
|
337
|
+
| Anatomy | `anatomy` |
|
|
338
|
+
| Variants (`theme`) | `variants` |
|
|
339
|
+
| Sizes (no size API) | `design-specifications` |
|
|
340
|
+
| States | `states` |
|
|
341
|
+
| Best practices / content rules | `guidelines` (+ `content`) |
|
|
342
|
+
| Accessibility | `accessibility` |
|
|
343
|
+
| Examples | `sections` |
|
|
344
|
+
| For agents | `agentDocumentBlocks` |
|
|
345
|
+
| Props detail | `api` in JSON + Joy MCP |
|
|
346
|
+
| Imports | `imports` in JSON |
|