@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,351 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: VJoySelectableItem
|
|
3
|
+
status: deprecated
|
|
4
|
+
language: en
|
|
5
|
+
dsds: JoySelectableItem.dsds.json
|
|
6
|
+
dsdsVersion: "0.15.2"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Selectable item
|
|
10
|
+
|
|
11
|
+
Use **`VJoySelectableItem`** when a choice should look like a **selectable tile** — a bordered card the user clicks to select or deselect — rather than a classic radio circle or checkbox row.
|
|
12
|
+
|
|
13
|
+
In single-selection mode it behaves like a radio (one value per `name` group). In multi-selection mode it embeds `VJoyCheckbox` inside the tile. In product code, compose items almost always through **`VJoySelectableItemGroup`**; use the item alone only when you own grouping, legend, and validation yourself.
|
|
14
|
+
|
|
15
|
+
> **Deprecated:** `VJoySelectableItem` and `VJoySelectableItemGroup` are deprecated. For new work, prefer `VJoyRadio` / `VJoyRadioGroup` (single choice), `VJoyCheckboxCard` (card-style booleans / multi-select), or `VJoyButtonCard` (navigation or action, not value selection). Keep this component only for maintaining existing flows until migration.
|
|
16
|
+
|
|
17
|
+
| | |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| **Code** | `VJoySelectableItem` from `@maltjoy/core-vue` |
|
|
20
|
+
| **Design** | [ZeroHeight — Selectable item](https://zeroheight.com/6bf479565/p/24f9a1-selectable-item) |
|
|
21
|
+
| **Storybook** | [VJoySelectableItemGroup docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-forms-vjoyselectableitemgroup--docs) (`VJoySelectableItem` is a subcomponent) |
|
|
22
|
+
| **DSDS (machine)** | [`JoySelectableItem.dsds.json`](./JoySelectableItem.dsds.json) |
|
|
23
|
+
| **Live API** | `joy_get_component_info("VJoySelectableItem")` (when indexed) — until then, read `VJoySelectableItem.vue` and `VJoySelectableItem.types.ts` |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## When to use this component
|
|
28
|
+
|
|
29
|
+
Use a selectable item when options should read as **peer tiles** in a horizontal or wrapped row — for example picking a plan cadence, a delivery speed, or a small set of filters where each option has a short label and optional sublabel.
|
|
30
|
+
|
|
31
|
+
Tiles work best with **2–5** options visible at once. Pair them inside `VJoySelectableItemGroup` so the fieldset, group label, `v-model:value`, and `invalid` / `errorMessage` are handled consistently.
|
|
32
|
+
|
|
33
|
+
### When not to use this component
|
|
34
|
+
|
|
35
|
+
| Need | Use instead |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| New feature work (any card picker) | `VJoyRadio` + `outline`, `VJoyCheckboxCard`, or `VJoyButtonCard` per interaction type |
|
|
38
|
+
| Exactly one choice, compact list (no tile chrome) | `VJoyRadio` inside `VJoyRadioGroup` |
|
|
39
|
+
| Multiple independent checkboxes in a list | `VJoyCheckbox` / `VJoyCheckboxGroup` or `VJoyCheckboxCard` |
|
|
40
|
+
| Long or searchable option lists | `VJoySelect` |
|
|
41
|
+
| Card that navigates or runs an action | `VJoyButtonCard` |
|
|
42
|
+
| Custom clickable `<div>` tiles | Still use a Joy selection component + tokens — don’t reinvent tile chrome |
|
|
43
|
+
|
|
44
|
+
> **Removed from the API:** the `ai` visual variant existed in older releases and has been removed. Do not emit `variant="ai"` or custom AI tile styling on this component.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## How it works
|
|
49
|
+
|
|
50
|
+
### Anatomy
|
|
51
|
+
|
|
52
|
+
| Part | Required? | What it is |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| Wrapper | Yes | `.joy-selectable-item__wrapper` — click target and focus orchestration |
|
|
55
|
+
| Native control | Yes* | Hidden `<input type="radio">` when `multiple` is false; `VJoyCheckbox` when `multiple` is true |
|
|
56
|
+
| Tile surface | Yes | `.joy-selectable-item` — bordered, centered label area |
|
|
57
|
+
| Primary label | Yes | Default slot (single mode) or checkbox label slot (multiple mode) |
|
|
58
|
+
| Sublabel | No | `selectable-item-sublabel` slot or `subLabel` prop — stays normal weight when selected |
|
|
59
|
+
| Focus ring | Yes | Outline on the tile when the native control is focused — don’t remove it in product CSS |
|
|
60
|
+
|
|
61
|
+
### Selection modes
|
|
62
|
+
|
|
63
|
+
Controlled by the `multiple` prop (usually set by `VJoySelectableItemGroup`).
|
|
64
|
+
|
|
65
|
+
| Mode | `multiple` | Control | Model on `@update:checked` |
|
|
66
|
+
| --- | --- | --- | --- |
|
|
67
|
+
| Single | `false` (default) | Hidden radio + `<label for="…">` | Emits the option `value` when selected; emits empty string when the user clicks the already-selected tile (deselect) |
|
|
68
|
+
| Multiple | `true` | `VJoyCheckbox` with `display-focus="false"` | Emits boolean checked state (group aggregates into a `value` array) |
|
|
69
|
+
|
|
70
|
+
### Default values
|
|
71
|
+
|
|
72
|
+
- `disabled` defaults to **`false`**
|
|
73
|
+
- `invalid` defaults to **`false`**
|
|
74
|
+
- `multiple` defaults to **`false`**
|
|
75
|
+
- `required` defaults to **`false`**
|
|
76
|
+
- `id` — auto-generated via Vue `useId()` when omitted; **SHOULD** set explicitly when not used inside a group
|
|
77
|
+
- `checked` — controlled from parent / group; no implicit default in the component
|
|
78
|
+
|
|
79
|
+
Props, events, and slots: use Joy MCP (when available), Storybook, or `VJoySelectableItem.vue` — this page focuses on **when** and **how**, not a full API dump.
|
|
80
|
+
|
|
81
|
+
### Grouping
|
|
82
|
+
|
|
83
|
+
- **SHOULD** render selectable items only inside `VJoySelectableItemGroup` in product forms.
|
|
84
|
+
- **MUST** give siblings the same `name` in single-selection mode so the browser treats them as one radio group.
|
|
85
|
+
- **MUST** set a distinct `value` (and stable `id`) per tile.
|
|
86
|
+
- Set group-level `invalid` and `errorMessage` on `VJoySelectableItemGroup`; the group forwards `invalid` to each item.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Sizes
|
|
91
|
+
|
|
92
|
+
There is **no `size` prop**. Tile dimensions come from Joy tokens inside `JoySelectableItem.scss` — **don’t hardcode tile width or height** in apps.
|
|
93
|
+
|
|
94
|
+
| Aspect | Guidance |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| Min height | `--selectable-item-min-height` (54px) on single-selection tiles |
|
|
97
|
+
| Default width | `--selectable-item-flex-rule: 0 0 215px` per tile in a single-selection group |
|
|
98
|
+
| Full-width row | Set `fullWidth` on `VJoySelectableItemGroup` (single selection only) — tiles grow with `flex: 1` |
|
|
99
|
+
| Typography | `--joy-font-size-primary-300`; label bold when checked |
|
|
100
|
+
| Spacing | Group gap uses `--joy-core-spacing-5`; tile padding uses `--joy-core-spacing-3` (single) or checkbox label padding (multiple) |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## States
|
|
105
|
+
|
|
106
|
+
| State | What the user sees / can do |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| Default (unchecked) | Neutral border (`--joy-color-neutral-30`); normal label weight |
|
|
109
|
+
| Checked | Secondary border and text (`--joy-color-secondary-50`); primary label **bold**; sublabel stays normal weight |
|
|
110
|
+
| Hover | Border accent on enabled tiles (pointer devices) |
|
|
111
|
+
| Focus | Visible outline on the tile (`--joy-form-field-focus-width` / `--joy-form-field-focus-color`) |
|
|
112
|
+
| Disabled | Muted text and neutral background; `not-allowed` cursor — **explain why** when the reason isn’t obvious |
|
|
113
|
+
| Invalid | Error border on the tile (`--joy-color-error-50`); pair with `errorMessage` on the group — don’t rely on color alone |
|
|
114
|
+
| Focusing (wrapper) | `joy-selectable-item--focusing` while the inner control is focused |
|
|
115
|
+
|
|
116
|
+
In single-selection mode, clicking a **checked** tile clears the selection (emits `''`). Confirm product rules allow “no selection” before relying on this behavior.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Best practices
|
|
121
|
+
|
|
122
|
+
Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
|
|
123
|
+
|
|
124
|
+
### Selection and hierarchy
|
|
125
|
+
|
|
126
|
+
- **SHOULD** offer **2–5** tiles; beyond that, prefer `VJoySelect` or a vertical radio/checkbox group.
|
|
127
|
+
- **SHOULD** pre-select a sensible default when one option is clearly recommended.
|
|
128
|
+
- **SHOULD** keep option labels parallel (same grammar and similar length).
|
|
129
|
+
- **MUST NOT** use selectable items for navigation or one-off actions — use `VJoyButtonCard`.
|
|
130
|
+
- **MUST NOT** build new features on this deprecated API — migrate to `VJoyRadio`, `VJoyCheckboxCard`, or `VJoyButtonCard`.
|
|
131
|
+
|
|
132
|
+
### Implementation
|
|
133
|
+
|
|
134
|
+
- **MUST** bind `checked` and listen for `@update:checked` (or use `VJoySelectableItemGroup` with `v-model:value`).
|
|
135
|
+
- **MUST** use only documented props from `VJoySelectableItem.vue` / `TVJoySelectableItemProps`.
|
|
136
|
+
- **MUST** style with Joy tokens — no custom `.joy-selectable-item` chrome overrides.
|
|
137
|
+
- **MUST NOT** invent props (`variant`, `size`, `ai`, `color`, …).
|
|
138
|
+
- **MUST** set `value` on every item used in forms.
|
|
139
|
+
- **SHOULD** put primary copy in the default slot; `label` prop is not rendered by the Vue component — use the slot (or the group’s `label` / `item` slot).
|
|
140
|
+
- **SHOULD** use `subLabel` or `#selectable-item-sublabel` for secondary detail, not a second line in the primary label.
|
|
141
|
+
|
|
142
|
+
### Interaction
|
|
143
|
+
|
|
144
|
+
- **MUST** set `disabled` on tiles the user cannot choose.
|
|
145
|
+
- **SHOULD** explain disabled tiles when the restriction isn’t clear from context.
|
|
146
|
+
- **SHOULD** use the group `item` slot (with `VJoyIcon` etc.) for rich tile content instead of bolting icons onto custom markup.
|
|
147
|
+
|
|
148
|
+
### Do / don’t
|
|
149
|
+
|
|
150
|
+
| Do | Don’t |
|
|
151
|
+
| --- | --- |
|
|
152
|
+
| `VJoySelectableItemGroup` + `options` / `v-model:value` | Lone tiles without group legend or validation |
|
|
153
|
+
| Parallel short labels + sublabels | Long paragraphs inside the tile |
|
|
154
|
+
| `invalid` + `errorMessage` on the group | Red borders with no error text |
|
|
155
|
+
| Migrate new work to Radio / CheckboxCard / ButtonCard | New `VJoySelectableItem` usage |
|
|
156
|
+
| `fullWidth` for equal-width single-select rows | Hardcoded `215px` tile widths in app CSS |
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Content
|
|
161
|
+
|
|
162
|
+
Tile copy should make each option distinct at a glance and read consistently as a set.
|
|
163
|
+
|
|
164
|
+
### Label formula
|
|
165
|
+
|
|
166
|
+
**Short noun phrase or concise statement** naming the option:
|
|
167
|
+
|
|
168
|
+
- ✅ Monthly
|
|
169
|
+
- ✅ Express delivery
|
|
170
|
+
- ✅ PDF export
|
|
171
|
+
|
|
172
|
+
Use the **same part of speech** across siblings.
|
|
173
|
+
|
|
174
|
+
### Sublabel
|
|
175
|
+
|
|
176
|
+
One line of supporting detail — price, timing, eligibility. Sublabels stay regular weight when the tile is selected so the primary label carries emphasis.
|
|
177
|
+
|
|
178
|
+
### Writing rules
|
|
179
|
+
|
|
180
|
+
| Strength | Rule |
|
|
181
|
+
| --- | --- |
|
|
182
|
+
| **MUST** | Label the **option**, not the action — ✅ `Standard` · ❌ `Select standard` |
|
|
183
|
+
| **MUST** | Keep primary labels ≤ ~30 characters; move detail to sublabel |
|
|
184
|
+
| **MUST** | Use sentence case for labels and sublabels |
|
|
185
|
+
| **MUST NOT** | Repeat the group legend in every tile label |
|
|
186
|
+
| **MUST NOT** | Use Yes / No as two tile labels when the question isn’t literally binary — name the outcomes |
|
|
187
|
+
| **SHOULD** | Put prices or counts in the sublabel, not the main label |
|
|
188
|
+
| **SHOULD** | Write the group `label` as a question or instruction (`Billing frequency`, `Export format`) |
|
|
189
|
+
| **SHOULD** | Keep `value` a stable programmatic key; localize slot content only |
|
|
190
|
+
|
|
191
|
+
### Patterns
|
|
192
|
+
|
|
193
|
+
**Plan / tier pickers** — primary label = plan name; sublabel = price or cadence; optional icon via group `#item` slot.
|
|
194
|
+
|
|
195
|
+
**Validation** — group `label` + `errorMessage` when `invalid`: ✅ `Choose a delivery speed` + `Select one option to continue`.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Accessibility
|
|
200
|
+
|
|
201
|
+
Behaviors (what must happen), not slogans.
|
|
202
|
+
|
|
203
|
+
| Behavior | Expectation |
|
|
204
|
+
| --- | --- |
|
|
205
|
+
| Role | Native `radio` (single) or `checkbox` (multiple) — not a clickable `<div>` alone |
|
|
206
|
+
| Name | Primary label from default slot (or checkbox label in multiple mode); group name from `VJoySelectableItemGroup` `label` / external `VJoyLabel` |
|
|
207
|
+
| Keyboard | **Space** toggles focused control; **Arrow keys** move between radios in the same `name` group (single mode) |
|
|
208
|
+
| Focus | Visible focus ring on the tile; checkbox inner focus ring suppressed via `display-focus="false"` to avoid double rings |
|
|
209
|
+
| Disabled | `disabled` on the native control; `aria-disabled` on the wrapper when disabled |
|
|
210
|
+
| Invalid | Error styling plus visible `errorMessage` from the group |
|
|
211
|
+
| Grouping | `VJoySelectableItemGroup` renders a `<fieldset>`; associate legend via `VJoyLabel` |
|
|
212
|
+
|
|
213
|
+
Target: **WCAG 2.2 AA** where the product requires it.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Examples
|
|
218
|
+
|
|
219
|
+
```vue
|
|
220
|
+
<script setup lang="ts">
|
|
221
|
+
import { ref } from 'vue'
|
|
222
|
+
import {
|
|
223
|
+
VJoySelectableItem,
|
|
224
|
+
VJoySelectableItemGroup,
|
|
225
|
+
type TJoySelectableItemOption,
|
|
226
|
+
} from '@maltjoy/core-vue'
|
|
227
|
+
|
|
228
|
+
const plan = ref('monthly')
|
|
229
|
+
|
|
230
|
+
const options: TJoySelectableItemOption[] = [
|
|
231
|
+
{ id: 'plan-monthly', value: 'monthly', label: 'Monthly', subLabel: 'Billed each month' },
|
|
232
|
+
{ id: 'plan-yearly', value: 'yearly', label: 'Yearly', subLabel: 'Save 20%' },
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
// Standalone single-selection tile (unusual — prefer the group)
|
|
236
|
+
const standalone = ref('a')
|
|
237
|
+
</script>
|
|
238
|
+
|
|
239
|
+
<template>
|
|
240
|
+
<!-- Recommended: group + v-model:value -->
|
|
241
|
+
<VJoySelectableItemGroup
|
|
242
|
+
v-model:value="plan"
|
|
243
|
+
label="Billing frequency"
|
|
244
|
+
:options="options"
|
|
245
|
+
/>
|
|
246
|
+
|
|
247
|
+
<!-- Multi-select -->
|
|
248
|
+
<VJoySelectableItemGroup
|
|
249
|
+
v-model:value="['pdf']"
|
|
250
|
+
multiple
|
|
251
|
+
label="Export formats"
|
|
252
|
+
:options="[
|
|
253
|
+
{ id: 'fmt-pdf', value: 'pdf', label: 'PDF' },
|
|
254
|
+
{ id: 'fmt-docx', value: 'docx', label: 'DOCX' },
|
|
255
|
+
]"
|
|
256
|
+
/>
|
|
257
|
+
|
|
258
|
+
<!-- Custom tile body via item slot -->
|
|
259
|
+
<VJoySelectableItemGroup v-model:value="plan" :options="options">
|
|
260
|
+
<template #item="option">
|
|
261
|
+
<strong>{{ option.label }}</strong>
|
|
262
|
+
</template>
|
|
263
|
+
</VJoySelectableItemGroup>
|
|
264
|
+
|
|
265
|
+
<!-- Standalone item (you manage group semantics) -->
|
|
266
|
+
<VJoySelectableItem
|
|
267
|
+
id="tile-a"
|
|
268
|
+
name="letters"
|
|
269
|
+
value="a"
|
|
270
|
+
:checked="standalone === 'a'"
|
|
271
|
+
@update:checked="(v) => { standalone = v as string }"
|
|
272
|
+
>
|
|
273
|
+
Option A
|
|
274
|
+
<template #selectable-item-sublabel>Detail for A</template>
|
|
275
|
+
</VJoySelectableItem>
|
|
276
|
+
</template>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
In Nuxt (or apps with auto-import), omit the import if components are already global.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Related
|
|
284
|
+
|
|
285
|
+
| Component | Use when |
|
|
286
|
+
| --- | --- |
|
|
287
|
+
| `VJoySelectableItemGroup` | Fieldset, options array, `v-model:value`, `invalid`, `fullWidth` |
|
|
288
|
+
| `VJoyRadio` / `VJoyRadioGroup` | **Preferred** replacement for single selection (including `outline` cards) |
|
|
289
|
+
| `VJoyCheckboxCard` | **Preferred** replacement for card-style checkbox / multi-select |
|
|
290
|
+
| `VJoyButtonCard` | Card that navigates or triggers an action |
|
|
291
|
+
| `VJoyCheckbox` | Embedded inside multiple-mode tiles (`display-focus="false"`) |
|
|
292
|
+
| `VJoyLabel` / `VJoyFormError` | External legend or group-level error display |
|
|
293
|
+
| `VJoySelect` | Long dropdown lists instead of tiles |
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## For agents
|
|
298
|
+
|
|
299
|
+
Maps to `agentDocumentBlocks` in [`JoySelectableItem.dsds.json`](./JoySelectableItem.dsds.json). **Do not use this component for new UI** — emit replacements below.
|
|
300
|
+
|
|
301
|
+
### Pick the right component
|
|
302
|
+
|
|
303
|
+
| If the intent is… | Emit… | Do not emit… |
|
|
304
|
+
| --- | --- | --- |
|
|
305
|
+
| New single-choice card picker | `VJoyRadio` `theme="outline"` in `VJoyRadioGroup` | `VJoySelectableItem` |
|
|
306
|
+
| New multi-select card picker | `VJoyCheckboxCard` / `VJoyCheckboxGroup` | `VJoySelectableItemGroup` `multiple` |
|
|
307
|
+
| Card link or CTA | `VJoyButtonCard` | Selectable item tiles |
|
|
308
|
+
| Maintain legacy tile group | `VJoySelectableItemGroup` + options | Custom div tiles |
|
|
309
|
+
| Rich icon in tile | `#item` slot on group | Invented `icon` prop on item (not rendered) |
|
|
310
|
+
|
|
311
|
+
### Hard rules
|
|
312
|
+
|
|
313
|
+
1. **MUST** read `VJoySelectableItem.vue` and `VJoySelectableItem.types.ts` (or MCP when available) before generating props.
|
|
314
|
+
2. **MUST** prefer `VJoySelectableItemGroup` over standalone items in forms.
|
|
315
|
+
3. **MUST** set `id` and `value` on every option object; shared `name` for single-selection siblings.
|
|
316
|
+
4. **MUST** bind group state with `v-model:value` (`string` single, `array` multiple).
|
|
317
|
+
5. **MUST NOT** invent props — no `variant`, `size`, or `ai`.
|
|
318
|
+
6. **MUST NOT** override `.joy-selectable-item` border, padding, min-height, or flex basis in product CSS.
|
|
319
|
+
7. **MUST NOT** use for new features — deprecated in favor of Radio / CheckboxCard / ButtonCard.
|
|
320
|
+
8. **SHOULD** set `invalid` and `errorMessage` on the group, not only on items.
|
|
321
|
+
9. **SHOULD** use `subLabel` / `#selectable-item-sublabel` for secondary text.
|
|
322
|
+
|
|
323
|
+
### Checklist
|
|
324
|
+
|
|
325
|
+
- [ ] Confirmed legacy maintenance (not greenfield) or documented migration target
|
|
326
|
+
- [ ] `VJoySelectableItemGroup` used for form fields when possible
|
|
327
|
+
- [ ] Each option has `id`, `value`, and label content in slot / `options`
|
|
328
|
+
- [ ] `v-model:value` type matches `multiple` (`string` vs `array`)
|
|
329
|
+
- [ ] No invented props / no custom tile chrome CSS
|
|
330
|
+
- [ ] Group `label` + `errorMessage` when `invalid`
|
|
331
|
+
- [ ] Parallel concise labels; detail in sublabel
|
|
332
|
+
- [ ] Replacement component identified for new work
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## DSDS mapping
|
|
337
|
+
|
|
338
|
+
This Markdown mirrors [`JoySelectableItem.dsds.json`](./JoySelectableItem.dsds.json) (DSDS **0.15.2**).
|
|
339
|
+
|
|
340
|
+
| Section here | DSDS block |
|
|
341
|
+
| --- | --- |
|
|
342
|
+
| When to use / not | `use-cases` |
|
|
343
|
+
| Anatomy / selection modes | `anatomy` |
|
|
344
|
+
| Sizes (no size API) | `design-specifications` |
|
|
345
|
+
| States | `states` |
|
|
346
|
+
| Best practices / content rules | `guidelines` (+ `content`) |
|
|
347
|
+
| Accessibility | `accessibility` |
|
|
348
|
+
| Examples | `sections` |
|
|
349
|
+
| For agents | `agentDocumentBlocks` |
|
|
350
|
+
| Props detail | `api` in JSON + Joy MCP |
|
|
351
|
+
| Imports | `imports` in JSON |
|