@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,352 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: VJoyMenuItem
|
|
3
|
+
status: draft
|
|
4
|
+
language: en
|
|
5
|
+
dsds: JoyMenuItem.dsds.json
|
|
6
|
+
dsdsVersion: "0.15.2"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Menu item
|
|
10
|
+
|
|
11
|
+
Use **`VJoyMenuItem`** when you need a single row inside a navigation menu — a labeled choice that navigates somewhere, fires an in-page action, or shows selection and status at a glance.
|
|
12
|
+
|
|
13
|
+
Each item renders as a `role="menuitem"` list row with optional leading icon, required text label, and optional trailing status icon. The inner control can be a native link (`<a>`), a `<button>`, or a router component (`RouterLink`, `NuxtLink`) passed through the `type` prop.
|
|
14
|
+
|
|
15
|
+
| | |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| **Code** | `VJoyMenuItem` from `@maltjoy/core-vue` |
|
|
18
|
+
| **Design** | [Figma — Joy components](https://www.figma.com/design/jfndp2QBJGS9yeuWbeGuvD/) |
|
|
19
|
+
| **Storybook** | [VJoyMenu docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-navigation-vjoymenu--docs) (see **WithSubItem** story) |
|
|
20
|
+
| **DSDS (machine)** | [`JoyMenuItem.dsds.json`](./JoyMenuItem.dsds.json) |
|
|
21
|
+
| **Live API** | `joy_get_component_info("VJoyMenuItem")` — if unavailable, read `VJoyMenuItem.vue` and `JoyMenu.metadata.md` |
|
|
22
|
+
|
|
23
|
+
> **MCP note:** Joy MCP may not return `VJoyMenuItem` API yet (docs generation is broken for this component). Use `VJoyMenuItem.vue`, `JoyMenu.metadata.md`, or Storybook **WithSubItem** as the source of truth until MCP is updated.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## When to use this component
|
|
28
|
+
|
|
29
|
+
Use a menu item when building a **persistent navigation list** inside `VJoyMenu` — for example app sidebar links, station pickers, or settings sections where each row is a destination or action.
|
|
30
|
+
|
|
31
|
+
`VJoyMenuItem` is the preferred way to declare menu rows (icons, current state, disabled rows, router links). Compose one or more items in the **default slot** of `VJoyMenu`.
|
|
32
|
+
|
|
33
|
+
### When not to use this component
|
|
34
|
+
|
|
35
|
+
| Need | Use instead |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| Contextual overflow / action menu from a trigger | `VJoyDropdown` + `VJoyDropdownList` |
|
|
38
|
+
| Labeled single-select form field | `VJoySelect` |
|
|
39
|
+
| Tabbed content switching on the same surface | `VJoyTabs` |
|
|
40
|
+
| Standalone CTA or form submit | `VJoyButton` |
|
|
41
|
+
| Data list rows with rich left/right content | `VJoyListItem` |
|
|
42
|
+
| Custom `<li>` markup with ad-hoc menu styling | Still use `VJoyMenuItem` + Joy tokens — don’t reinvent menu row chrome |
|
|
43
|
+
|
|
44
|
+
> **Deprecated:** do not drive menu rows through `VJoyMenu`’s `items` prop for new work. That array API is deprecated — use `VJoyMenuItem` children in the default slot instead.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## How it works
|
|
49
|
+
|
|
50
|
+
### Anatomy
|
|
51
|
+
|
|
52
|
+
| Part | Required? | What it is |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| List row (`<li>`) | Yes | Outer `role="menuitem"` wrapper with `aria-label`, `aria-disabled`, and `aria-current` |
|
|
55
|
+
| Inner control | Yes | `.joy-menu-item__inner` — `<a>`, `<button>`, router component, or `<div>` when `disabled` |
|
|
56
|
+
| Label | Yes | Text from the `label` prop (`.joy-menu-item__label`) |
|
|
57
|
+
| Leading icon | No | `VJoyIcon` when `icon` is set (`.joy-menu-item__icon-left`) |
|
|
58
|
+
| Status icon | No | Trailing `VJoyIcon` when `status` is set (`.joy-menu-item__icon-right`); color via `statusColor` |
|
|
59
|
+
| Focus ring | Yes | Inset `box-shadow` on inner `:focus` — don’t remove it in product CSS |
|
|
60
|
+
|
|
61
|
+
### Default values
|
|
62
|
+
|
|
63
|
+
- `type` defaults to **`'a'`** (native anchor)
|
|
64
|
+
- `disabled` defaults to **`false`**
|
|
65
|
+
- `current` defaults to **`false`**
|
|
66
|
+
- `href`, `icon`, `status`, `statusColor`, and `linkProps` are optional
|
|
67
|
+
|
|
68
|
+
Props, events, and composition: use Joy MCP (when available), `VJoyMenuItem.vue`, or Storybook — this page focuses on **when** and **how**, not a full API dump.
|
|
69
|
+
|
|
70
|
+
### Link types
|
|
71
|
+
|
|
72
|
+
| `type` | Inner element | Props / attrs |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `'a'` (default) | `<a href="…">` | `href` prop + native attrs via `$attrs` (`target`, `rel`, `data-*`, …) |
|
|
75
|
+
| `'button'` | `<button type="button">` | Native button attrs via `$attrs`; emits `click` |
|
|
76
|
+
| `RouterLink` / `NuxtLink` / custom component | Dynamic `:is="type"` | Pass routing props via `linkProps` (e.g. `{ to: '/' }`) |
|
|
77
|
+
|
|
78
|
+
When `disabled` is true, the inner control becomes a **non-interactive `<div>`** — clicks do not emit `click`.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## States
|
|
83
|
+
|
|
84
|
+
| State | What the user sees / can do |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| Default | Neutral text (`--joy-color-neutral-60`); row is interactive |
|
|
87
|
+
| Hover | Light secondary background (`--joy-color-secondary-10`) on the inner control |
|
|
88
|
+
| Focus | Secondary background + inset information focus ring (`--joy-color-information-50`) |
|
|
89
|
+
| Current | `current` prop — secondary background and secondary text color; `aria-current` on the row |
|
|
90
|
+
| Disabled | Muted text, disabled background (`--joy-color-state-disabled-background`), `cursor: not-allowed`; inner is `<div>`; `aria-disabled="true"`; no `click` emission |
|
|
91
|
+
|
|
92
|
+
Long labels truncate with ellipsis; the full string is available via the native `title` attribute on the row.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Best practices
|
|
97
|
+
|
|
98
|
+
Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
|
|
99
|
+
|
|
100
|
+
### Composition
|
|
101
|
+
|
|
102
|
+
- **MUST** place `VJoyMenuItem` inside `VJoyMenu` (default slot) so keyboard navigation and `role="menu"` semantics stay correct.
|
|
103
|
+
- **MUST** set `aria-label` on `VJoyMenu` when the menu’s purpose isn’t obvious from visible context alone.
|
|
104
|
+
- **SHOULD** use at most one `current` item per menu when indicating the active destination.
|
|
105
|
+
- **SHOULD** order items logically — primary destinations first, destructive or rare actions last.
|
|
106
|
+
|
|
107
|
+
### Implementation
|
|
108
|
+
|
|
109
|
+
- **MUST** use only documented props from `VJoyMenuItem.vue` / `JoyMenu.metadata.md` — no invented props.
|
|
110
|
+
- **MUST** style with Joy tokens — no custom `.joy-menu-item` chrome overrides.
|
|
111
|
+
- **MUST** pass `href` when `type` is `'a'` and the row navigates.
|
|
112
|
+
- **MUST** pass `linkProps` when `type` is `RouterLink`, `NuxtLink`, or another component.
|
|
113
|
+
- **MUST NOT** use `VJoyMenu`’s deprecated `items` array for new menus.
|
|
114
|
+
- **MUST NOT** hardcode row height — the component uses `--joy-form-field-height-medium`.
|
|
115
|
+
- **SHOULD** pass native HTML attributes (`target`, `rel`, `data-test-id`, …) as attributes on `VJoyMenuItem` when `type` is `'a'` or `'button'` (they flow through `$attrs`).
|
|
116
|
+
|
|
117
|
+
### Icons and status
|
|
118
|
+
|
|
119
|
+
- **SHOULD** use `icon` for wayfinding (user, settings, section glyph) — not decoration alone.
|
|
120
|
+
- **SHOULD** use `status` + `statusColor` for semantic feedback (check, warning, lock) — keep one trailing icon per row.
|
|
121
|
+
- **MUST NOT** rely on icon color alone to convey meaning — the `label` must still describe the action or destination.
|
|
122
|
+
|
|
123
|
+
### Interaction
|
|
124
|
+
|
|
125
|
+
- **MUST** set `disabled` when the row must not activate; explain why elsewhere if the reason isn’t obvious.
|
|
126
|
+
- **SHOULD** use `type="button"` + `@click` for in-page actions that do not navigate.
|
|
127
|
+
- **SHOULD** use `type` router component + `linkProps` for SPA navigation instead of `<a>` + manual router pushes.
|
|
128
|
+
|
|
129
|
+
### Do / don’t
|
|
130
|
+
|
|
131
|
+
| Do | Don’t |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| `VJoyMenu` + `VJoyMenuItem` slot children | Deprecated `items` prop on `VJoyMenu` |
|
|
134
|
+
| `linkProps` with `NuxtLink` / `RouterLink` | Plain `<a>` + `@click` router hacks |
|
|
135
|
+
| `current` for the active nav target | Multiple `current` rows without reason |
|
|
136
|
+
| Concise `label` text (1–3 words) | Vague labels (`Click here`, `Menu`) |
|
|
137
|
+
| `status` for lock / check / warning | Random decorative trailing icons |
|
|
138
|
+
| Explain disabled rows when needed | Disabled row with no context |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Content
|
|
143
|
+
|
|
144
|
+
Menu items are scanned quickly. Labels must be short, parallel, and predictable.
|
|
145
|
+
|
|
146
|
+
### Label formula
|
|
147
|
+
|
|
148
|
+
**Navigation rows:** **noun or short noun phrase** — e.g. `Profile`, `Billing`, `Paris Montparnasse`.
|
|
149
|
+
|
|
150
|
+
**Action rows** (`type="button"`): **verb or verb + object** — e.g. `Sign out`, `Export data`.
|
|
151
|
+
|
|
152
|
+
Use **parallel grammar** across siblings (all nouns or all verbs — don’t mix styles in one menu).
|
|
153
|
+
|
|
154
|
+
### Writing rules
|
|
155
|
+
|
|
156
|
+
| Strength | Rule |
|
|
157
|
+
| --- | --- |
|
|
158
|
+
| **MUST** | `label` is required and becomes the accessible name (`aria-label` + `title`) |
|
|
159
|
+
| **MUST** | Keep labels concise; overflow shows ellipsis — don’t rely on truncation for critical detail |
|
|
160
|
+
| **MUST NOT** | Vague labels — ❌ `Click here`, ❌ `Options`, ❌ `Item 1` in product UI |
|
|
161
|
+
| **MUST NOT** | Duplicate the parent menu title on every row when context is already clear |
|
|
162
|
+
| **SHOULD** | 1–3 words per item |
|
|
163
|
+
| **SHOULD** | Sentence case for multi-word labels |
|
|
164
|
+
| **SHOULD** | Name destructive actions explicitly — ✅ `Delete project` · ❌ `Delete` alone in a mixed menu |
|
|
165
|
+
| **SHOULD** | Start each item the same way (all verbs or all nouns) for scannability |
|
|
166
|
+
|
|
167
|
+
### Patterns
|
|
168
|
+
|
|
169
|
+
**App / settings navigation** — Noun labels: `Account`, `Notifications`, `Privacy`. Mark the active section with `current`.
|
|
170
|
+
|
|
171
|
+
**Router-linked sidebar** — `:type="NuxtLink"` (or `RouterLink`) + `:link-props="{ to: '/path' }"`. Label names the destination page.
|
|
172
|
+
|
|
173
|
+
**Status feedback** — Trailing `status` icon with `statusColor`: check for completed setup, lock for gated features, warning for attention needed. Label still states the destination.
|
|
174
|
+
|
|
175
|
+
**Disabled rows** — Keep the label readable; use `status="lock"` when helpful. Explain gating in surrounding UI or a tooltip on a related control — not only via icon color.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Accessibility
|
|
180
|
+
|
|
181
|
+
Behaviors (what must happen), not slogans.
|
|
182
|
+
|
|
183
|
+
| Behavior | Expectation |
|
|
184
|
+
| --- | --- |
|
|
185
|
+
| Role | Outer `<li role="menuitem">` inside `VJoyMenu`’s `role="menu"` list |
|
|
186
|
+
| Name | From `label` (`aria-label` on the row; `title` for truncated text) |
|
|
187
|
+
| Current page | `current` sets `aria-current` on the row and visible current styling |
|
|
188
|
+
| Disabled | `aria-disabled="true"` on the row; inner becomes non-focusable `<div>` |
|
|
189
|
+
| Keyboard | Arrow keys, Home, End, Enter, and Space are handled by parent `VJoyMenu` — don’t reimplement list keyboard logic on individual items |
|
|
190
|
+
| Focus | Inner control shows visible focus ring — keep it in product CSS |
|
|
191
|
+
| Icons | Leading/trailing icons are decorative when `label` is present — Joy icons default to `aria-hidden` where appropriate |
|
|
192
|
+
| Links | Use real `<a href>` or router links — not clickable `<div>` except when `disabled` |
|
|
193
|
+
|
|
194
|
+
Target: **WCAG 2.2 AA** where the product requires it.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Examples
|
|
199
|
+
|
|
200
|
+
```vue
|
|
201
|
+
<script setup lang="ts">
|
|
202
|
+
import { VJoyMenu, VJoyMenuItem } from '@maltjoy/core-vue'
|
|
203
|
+
|
|
204
|
+
// Nuxt example — use your app's NuxtLink import
|
|
205
|
+
// import { NuxtLink } from '#components'
|
|
206
|
+
</script>
|
|
207
|
+
|
|
208
|
+
<template>
|
|
209
|
+
<VJoyMenu aria-label="Account sections">
|
|
210
|
+
<!-- Default: native link -->
|
|
211
|
+
<VJoyMenuItem label="Profile" href="/profile" icon="user" />
|
|
212
|
+
|
|
213
|
+
<!-- Current section -->
|
|
214
|
+
<VJoyMenuItem
|
|
215
|
+
label="Billing"
|
|
216
|
+
href="/billing"
|
|
217
|
+
icon="credit-card"
|
|
218
|
+
current
|
|
219
|
+
status="check-circle"
|
|
220
|
+
status-color="success"
|
|
221
|
+
/>
|
|
222
|
+
|
|
223
|
+
<!-- Opens in new tab -->
|
|
224
|
+
<VJoyMenuItem
|
|
225
|
+
label="Help center"
|
|
226
|
+
href="https://help.malt.com"
|
|
227
|
+
target="_blank"
|
|
228
|
+
rel="noopener noreferrer"
|
|
229
|
+
/>
|
|
230
|
+
|
|
231
|
+
<!-- In-page action -->
|
|
232
|
+
<VJoyMenuItem
|
|
233
|
+
label="Sign out"
|
|
234
|
+
type="button"
|
|
235
|
+
icon="logout"
|
|
236
|
+
@click="onSignOut"
|
|
237
|
+
/>
|
|
238
|
+
|
|
239
|
+
<!-- SPA navigation (Nuxt) -->
|
|
240
|
+
<!--
|
|
241
|
+
<VJoyMenuItem
|
|
242
|
+
label="Dashboard"
|
|
243
|
+
:type="NuxtLink"
|
|
244
|
+
:link-props="{ to: '/' }"
|
|
245
|
+
icon="home"
|
|
246
|
+
/>
|
|
247
|
+
-->
|
|
248
|
+
|
|
249
|
+
<!-- Disabled row -->
|
|
250
|
+
<VJoyMenuItem
|
|
251
|
+
label="Team settings"
|
|
252
|
+
disabled
|
|
253
|
+
icon="company-placeholder"
|
|
254
|
+
status="lock"
|
|
255
|
+
/>
|
|
256
|
+
</VJoyMenu>
|
|
257
|
+
</template>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
In Nuxt (or apps with auto-import), omit the import if `VJoyMenuItem` is already global.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Related
|
|
265
|
+
|
|
266
|
+
| Component | Use when |
|
|
267
|
+
| --- | --- |
|
|
268
|
+
| `VJoyMenu` | Parent list with keyboard navigation and `role="menu"` |
|
|
269
|
+
| `VJoyDropdown` | Contextual menus opened from a trigger |
|
|
270
|
+
| `VJoyDropdownList` | Option list UI without full menu semantics |
|
|
271
|
+
| `VJoyIcon` | Icon primitive used for `icon` and `status` |
|
|
272
|
+
| `VJoyButton` | Standalone actions outside a menu list |
|
|
273
|
+
| `VJoyTabs` | Switching content panels on the same surface |
|
|
274
|
+
| `VJoyTooltip` | Extra detail for disabled or icon-only adjacent controls |
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## For agents
|
|
279
|
+
|
|
280
|
+
Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoyMenuItem.dsds.json`](./JoyMenuItem.dsds.json).
|
|
281
|
+
|
|
282
|
+
### Pick the right component
|
|
283
|
+
|
|
284
|
+
| If the intent is… | Emit… | Do not emit… |
|
|
285
|
+
| --- | --- | --- |
|
|
286
|
+
| Row inside a nav / settings menu | `VJoyMenu` + `VJoyMenuItem` | Raw `<li>` with custom menu CSS |
|
|
287
|
+
| Overflow menu from a button | `VJoyDropdown` | `VJoyMenu` for kebab menus |
|
|
288
|
+
| SPA route in a menu | `VJoyMenuItem` + `:type="NuxtLink"` / `RouterLink` + `link-props` | `<a>` + `@click` router navigation |
|
|
289
|
+
| External URL | `VJoyMenuItem` + `href` (default `type="a"`) | `type="button"` + window.location |
|
|
290
|
+
| In-page menu action | `VJoyMenuItem` + `type="button"` + `@click` | `href="#"` + click handler |
|
|
291
|
+
| Active nav destination | `current` on one item | Custom “selected” CSS classes |
|
|
292
|
+
| Deprecated data-driven menu | `VJoyMenuItem` children | `VJoyMenu` `items` prop |
|
|
293
|
+
|
|
294
|
+
### Hard rules
|
|
295
|
+
|
|
296
|
+
1. **MUST** read `VJoyMenuItem.vue` and/or `JoyMenu.metadata.md` before generating props (`joy_get_component_info` may not work yet).
|
|
297
|
+
2. **MUST** wrap items in `VJoyMenu` with an `aria-label` when context is not self-evident.
|
|
298
|
+
3. **MUST** set `label` on every item — there is no default slot for label text.
|
|
299
|
+
4. **MUST** use `linkProps` when `type` is a Vue/router component; use `href` + `$attrs` when `type` is `'a'`.
|
|
300
|
+
5. **MUST** use `type="button"` for actions that emit `click` without navigation.
|
|
301
|
+
6. **MUST NOT** invent props (`variant`, `size`, `selected`, `active`, …).
|
|
302
|
+
7. **MUST NOT** override `.joy-menu-item` / `.joy-menu-item__inner` background, padding, height, or focus ring.
|
|
303
|
+
8. **MUST NOT** use `VJoyMenu` `items` prop in new code.
|
|
304
|
+
9. **SHOULD** set at most one `current` item per menu for active-route indication.
|
|
305
|
+
10. **SHOULD** use kebab-case in templates for multi-word props (`status-color` → `statusColor`).
|
|
306
|
+
|
|
307
|
+
### Documented props (from source)
|
|
308
|
+
|
|
309
|
+
| Prop | Type / notes |
|
|
310
|
+
| --- | --- |
|
|
311
|
+
| `label` | `string` — **required** |
|
|
312
|
+
| `href` | `string` — for `type="a"` |
|
|
313
|
+
| `icon` | `TJoyIconsNames` — leading icon |
|
|
314
|
+
| `current` | `boolean` — active / current destination |
|
|
315
|
+
| `disabled` | `boolean` |
|
|
316
|
+
| `status` | `TJoyIconsNames` — trailing status icon |
|
|
317
|
+
| `statusColor` | `TJoyIconsColors` — trailing icon color |
|
|
318
|
+
| `type` | `Component \| 'a' \| 'button'` — default `'a'` |
|
|
319
|
+
| `linkProps` | `ComponentProps<T>` — for non-native `type` |
|
|
320
|
+
|
|
321
|
+
**Event:** `click` — emitted on inner activation when not `disabled` (typically `type="button"`).
|
|
322
|
+
|
|
323
|
+
### Checklist
|
|
324
|
+
|
|
325
|
+
- [ ] Correct component (`VJoyMenuItem` inside `VJoyMenu`, or named alternative)
|
|
326
|
+
- [ ] Props from source / metadata only — no invented props
|
|
327
|
+
- [ ] `label` set on every row
|
|
328
|
+
- [ ] `href` or `linkProps` matches `type` (link vs router vs button)
|
|
329
|
+
- [ ] `current` used intentionally (≤ 1 per menu in most cases)
|
|
330
|
+
- [ ] `disabled` rows explained when reason isn’t obvious
|
|
331
|
+
- [ ] No custom `.joy-menu-item` chrome CSS
|
|
332
|
+
- [ ] No deprecated `VJoyMenu` `items` array for new menus
|
|
333
|
+
- [ ] Native attrs on `VJoyMenuItem` when using `'a'` or `'button'`
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## DSDS mapping
|
|
338
|
+
|
|
339
|
+
This Markdown mirrors [`JoyMenuItem.dsds.json`](./JoyMenuItem.dsds.json) (DSDS **0.15.2**).
|
|
340
|
+
|
|
341
|
+
| Section here | DSDS block |
|
|
342
|
+
| --- | --- |
|
|
343
|
+
| When to use / not | `use-cases` |
|
|
344
|
+
| Anatomy / link types | `anatomy` |
|
|
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 (when available) |
|
|
351
|
+
| Imports | `imports` in JSON |
|
|
352
|
+
| Token sizing | `design-specifications` in JSON (`--joy-form-field-height-medium`, color tokens) |
|