@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.
Files changed (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -3
  3. package/dist/doc/VJoyAdminBanner/guideline.md +284 -0
  4. package/dist/doc/VJoyAvailability/guideline.md +352 -0
  5. package/dist/doc/VJoyAvatar/guideline.md +192 -0
  6. package/dist/doc/VJoyAvatarsList/guideline.md +328 -0
  7. package/dist/doc/VJoyBadge/guideline.md +169 -0
  8. package/dist/doc/VJoyBadgeLevel/guideline.md +166 -0
  9. package/dist/doc/VJoyBlockSkeleton/guideline.md +150 -0
  10. package/dist/doc/VJoyBottomSheet/guideline.md +345 -0
  11. package/dist/doc/VJoyBottomSheetTrigger/guideline.md +156 -0
  12. package/dist/doc/VJoyButton/guideline.md +371 -0
  13. package/dist/doc/VJoyButton/metadata.json +0 -16
  14. package/dist/doc/VJoyButtonCard/guideline.md +384 -0
  15. package/dist/doc/VJoyCheckbox/guideline.md +317 -0
  16. package/dist/doc/VJoyCheckboxCard/guideline.md +381 -0
  17. package/dist/doc/VJoyCheckboxGroup/guideline.md +363 -0
  18. package/dist/doc/VJoyCheckboxGroup/metadata.json +7 -7
  19. package/dist/doc/VJoyCollapse/guideline.md +362 -0
  20. package/dist/doc/VJoyCollapseItem/guideline.md +394 -0
  21. package/dist/doc/VJoyCompanyAvatar/guideline.md +162 -0
  22. package/dist/doc/VJoyDialog/guideline.md +425 -0
  23. package/dist/doc/VJoyDialogTrigger/guideline.md +186 -0
  24. package/dist/doc/VJoyDot/guideline.md +177 -0
  25. package/dist/doc/VJoyDrawer/guideline.md +392 -0
  26. package/dist/doc/VJoyDrawerTrigger/guideline.md +145 -0
  27. package/dist/doc/VJoyDropdown/guideline.md +393 -0
  28. package/dist/doc/VJoyDropdown/metadata.json +6 -6
  29. package/dist/doc/VJoyDropdownList/guideline.md +396 -0
  30. package/dist/doc/VJoyDropdownList/metadata.json +7 -7
  31. package/dist/doc/VJoyDropzone/guideline.md +390 -0
  32. package/dist/doc/VJoyDropzone/metadata.json +36 -1
  33. package/dist/doc/VJoyFilterBar/guideline.md +419 -0
  34. package/dist/doc/VJoyFilterBarButton/guideline.md +359 -0
  35. package/dist/doc/VJoyFloatingActionsBar/guideline.md +372 -0
  36. package/dist/doc/VJoyFormError/guideline.md +325 -0
  37. package/dist/doc/VJoyFormError/metadata.json +0 -14
  38. package/dist/doc/VJoyFormFieldSkeleton/guideline.md +159 -0
  39. package/dist/doc/VJoyFormGroup/guideline.md +285 -0
  40. package/dist/doc/VJoyFunnel/guideline.md +349 -0
  41. package/dist/doc/VJoyFunnelFooter/guideline.md +178 -0
  42. package/dist/doc/VJoyFunnelHeader/guideline.md +204 -0
  43. package/dist/doc/VJoyHighlight/guideline.md +169 -0
  44. package/dist/doc/VJoyIcon/JoyIcon.types.ts +1 -1
  45. package/dist/doc/VJoyIcon/guideline.md +168 -0
  46. package/dist/doc/VJoyIcon/metadata.json +0 -1
  47. package/dist/doc/VJoyIconButton/guideline.md +169 -0
  48. package/dist/doc/VJoyIconButton/metadata.json +1 -1
  49. package/dist/doc/VJoyIndicator/guideline.md +168 -0
  50. package/dist/doc/VJoyIndicators/guideline.md +183 -0
  51. package/dist/doc/VJoyInput/JoyInput.types.ts +1 -6
  52. package/dist/doc/VJoyInput/guideline.md +401 -0
  53. package/dist/doc/VJoyInput/metadata.json +5 -14
  54. package/dist/doc/VJoyInputDigit/guideline.md +381 -0
  55. package/dist/doc/VJoyInputNumber/guideline.md +414 -0
  56. package/dist/doc/VJoyInputNumber/metadata.json +22 -17
  57. package/dist/doc/VJoyInputPassword/guideline.md +379 -0
  58. package/dist/doc/VJoyInputPassword/metadata.json +0 -14
  59. package/dist/doc/VJoyLabel/guideline.md +200 -0
  60. package/dist/doc/VJoyLink/guideline.md +360 -0
  61. package/dist/doc/VJoyListItem/guideline.md +335 -0
  62. package/dist/doc/VJoyMenu/guideline.md +304 -0
  63. package/dist/doc/VJoyMenu/metadata.json +21 -21
  64. package/dist/doc/VJoyMenuItem/guideline.md +352 -0
  65. package/dist/doc/VJoyPagination/guideline.md +355 -0
  66. package/dist/doc/VJoyPanel/guideline.md +238 -0
  67. package/dist/doc/VJoyPanelSection/guideline.md +160 -0
  68. package/dist/doc/VJoyPasswordRequirement/guideline.md +336 -0
  69. package/dist/doc/VJoyPasswordRequirement/metadata.json +1 -1
  70. package/dist/doc/VJoyProductTour/JoyProductTour.types.ts +0 -1
  71. package/dist/doc/VJoyProductTour/guideline.md +455 -0
  72. package/dist/doc/VJoyProductTour/metadata.json +1 -1
  73. package/dist/doc/VJoyProductTourTrigger/guideline.md +167 -0
  74. package/dist/doc/VJoyProgressBar/guideline.md +348 -0
  75. package/dist/doc/VJoyRadio/guideline.md +346 -0
  76. package/dist/doc/VJoyRadioGroup/guideline.md +331 -0
  77. package/dist/doc/VJoyRatingStars/guideline.md +306 -0
  78. package/dist/doc/VJoyScreenLoader/guideline.md +172 -0
  79. package/dist/doc/VJoySelect/guideline.md +396 -0
  80. package/dist/doc/VJoySelectableItem/guideline.md +351 -0
  81. package/dist/doc/VJoySelectableItemGroup/guideline.md +370 -0
  82. package/dist/doc/VJoySeparator/guideline.md +161 -0
  83. package/dist/doc/VJoySnackbar/guideline.md +402 -0
  84. package/dist/doc/VJoySpinner/guideline.md +168 -0
  85. package/dist/doc/VJoyStep/guideline.md +182 -0
  86. package/dist/doc/VJoyStepper/guideline.md +390 -0
  87. package/dist/doc/VJoyTab/guideline.md +298 -0
  88. package/dist/doc/VJoyTabs/guideline.md +353 -0
  89. package/dist/doc/VJoyTag/guideline.md +226 -0
  90. package/dist/doc/VJoyTagsInput/guideline.md +387 -0
  91. package/dist/doc/VJoyTagsList/guideline.md +311 -0
  92. package/dist/doc/VJoyTagsList/metadata.json +27 -27
  93. package/dist/doc/VJoyTemplate/guideline.md +366 -0
  94. package/dist/doc/VJoyText/guideline.md +175 -0
  95. package/dist/doc/VJoyText/metadata.json +1 -1
  96. package/dist/doc/VJoyTextarea/guideline.md +378 -0
  97. package/dist/doc/VJoyTitleBrand/guideline.md +155 -0
  98. package/dist/doc/VJoyToggle/guideline.md +327 -0
  99. package/dist/doc/VJoyTooltip/guideline.md +322 -0
  100. package/dist/doc/VJoyUserCard/guideline.md +268 -0
  101. package/dist/doc/VJoyUserCard/metadata.json +3 -17
  102. package/dist/doc/VJoyWalkthrough/guideline.md +351 -0
  103. package/dist/doc/VJoyWalkthroughTrigger/guideline.md +171 -0
  104. package/dist/doc/VJoyWrapper/guideline.md +173 -0
  105. package/dist/doc/css-classes.json +1 -15
  106. package/dist/doc/design-tokens.json +1 -0
  107. package/dist/doc/index.json +20 -30
  108. package/dist/index.js +22 -2
  109. package/dist/index.js.map +1 -1
  110. package/dist/tools/get-component-guideline.d.ts +30 -0
  111. package/dist/tools/get-component-guideline.d.ts.map +1 -0
  112. package/dist/tools/get-component-guideline.js +51 -0
  113. package/dist/tools/get-component-guideline.js.map +1 -0
  114. package/dist/tools/get-documentation-page.d.ts +22 -0
  115. package/dist/tools/get-documentation-page.d.ts.map +1 -0
  116. package/dist/tools/get-documentation-page.js +21 -0
  117. package/dist/tools/get-documentation-page.js.map +1 -0
  118. package/dist/tools/list-documentation-pages.d.ts +22 -0
  119. package/dist/tools/list-documentation-pages.d.ts.map +1 -0
  120. package/dist/tools/list-documentation-pages.js +17 -0
  121. package/dist/tools/list-documentation-pages.js.map +1 -0
  122. package/dist/tools/search-documentation.d.ts +41 -0
  123. package/dist/tools/search-documentation.d.ts.map +1 -0
  124. package/dist/tools/search-documentation.js +25 -0
  125. package/dist/tools/search-documentation.js.map +1 -0
  126. package/dist/zeroheight-parser.d.ts +27 -0
  127. package/dist/zeroheight-parser.d.ts.map +1 -0
  128. package/dist/zeroheight-parser.js +77 -0
  129. package/dist/zeroheight-parser.js.map +1 -0
  130. package/package.json +10 -10
  131. package/dist/doc/VJoyMultiCheckbox/metadata.json +0 -332
@@ -0,0 +1,396 @@
1
+ ---
2
+ component: VJoyDropdownList
3
+ status: draft
4
+ language: en
5
+ dsds: JoyDropdownList.dsds.json
6
+ dsdsVersion: "0.15.2"
7
+ ---
8
+
9
+ # Dropdown list
10
+
11
+ Use **`VJoyDropdownList`** when you need the scrollable option panel of a dropdown — selectable rows, optional header/footer, and listbox keyboard support — without the trigger, overlay positioning, or open/close lifecycle.
12
+
13
+ It powers `VJoyDropdown` internally and can be mounted standalone when the parent already controls visibility and placement. Bind selection with `v-model`, pass an `options` array, or inject fully custom body content through slots.
14
+
15
+ | | |
16
+ | --- | --- |
17
+ | **Code** | `VJoyDropdownList` from `@maltjoy/core-vue` |
18
+ | **Design** | [Figma — Joy components](https://www.figma.com/design/jfndp2QBJGS9yeuWbeGuvD/) |
19
+ | **Storybook** | [VJoyDropdownList docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-forms-vjoydropdownlist--docs) |
20
+ | **DSDS (machine)** | [`JoyDropdownList.dsds.json`](./JoyDropdownList.dsds.json) |
21
+ | **Live API** | `joy_get_component_info("VJoyDropdownList")` |
22
+
23
+ ---
24
+
25
+ ## When to use this component
26
+
27
+ Use a dropdown list when a panel must show one or more selectable rows (or custom filter controls) inside an already-open overlay — for example the list body of `VJoyDropdown`, a filter bar panel you position yourself, or a popover whose open state lives in the parent.
28
+
29
+ It fits both **predefined option lists** (`options` + `v-model`) and **custom bodies** (`dropdown-list-items` slot with `VJoyCheckboxGroup` or other form controls).
30
+
31
+ ### When not to use this component
32
+
33
+ | Need | Use instead |
34
+ | --- | --- |
35
+ | Trigger + open/close + click-outside + focus return | `VJoyDropdown` |
36
+ | Labeled single-select form field (native select semantics) | `VJoySelect` |
37
+ | Persistent navigation menus | `VJoyMenu` + `VJoyMenuItem` |
38
+ | Every option visible at once (2–7 parallel choices) | `VJoyRadio` inside `VJoyRadioGroup` |
39
+ | Card-style selectable tiles | `VJoySelectableItemGroup` |
40
+ | Custom `position: absolute` panels with ad-hoc list markup | Still use `VJoyDropdownList` + Joy tokens — don’t reinvent list chrome |
41
+
42
+ ---
43
+
44
+ ## How it works
45
+
46
+ ### Anatomy
47
+
48
+ | Part | Required? | What it is |
49
+ | --- | --- | --- |
50
+ | Root container | Yes | `.joy-dropdown-list` — bordered panel with elevation and fixed `width` |
51
+ | Header | No | `.joy-dropdown-list__header` — optional reset CTA and/or `dropdown-list-header` slot |
52
+ | Reset control | No | Text button when `reset` is true; emits `reset` (does not clear parent state) |
53
+ | Items scroller | Yes | `.joy-dropdown-list__items` — scrollable area capped by `height` |
54
+ | Options list | No* | `<ul role="listbox">` with `<li role="option">` rows when `options` is non-empty |
55
+ | Custom items body | No* | `dropdown-list-items` slot — replaces the built-in list when `options` is empty |
56
+ | Option label | No | Default `<span>{{ option.label }}</span>` or scoped `label` slot per row |
57
+ | Footer | No | `.joy-dropdown-list__footer` via `dropdown-list-footer` slot |
58
+
59
+ \* Use either the built-in `options` list **or** `dropdown-list-items`, not both for the same purpose.
60
+
61
+ ### Default values
62
+
63
+ - `size` defaults to **`medium`**
64
+ - `width` defaults to **`300`** (px, applied as inline `width` / `max-width`)
65
+ - `height` defaults to **`300`** (px max-height of the scrollable items area)
66
+ - `options` defaults to **`[]`**
67
+ - `reset` defaults to **`false`**
68
+ - `modelValue` has no default — bind when selection should persist
69
+
70
+ Props, events, and slots: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
71
+
72
+ ### Option shape
73
+
74
+ Each entry in `options` should provide:
75
+
76
+ - **`id`** (required, unique) — row identity and default `v-model` value
77
+ - **`label`** (required) — visible text; also used for `title` tooltip and `aria-label` on the row
78
+ - **`value`** (optional) — alternate `v-model` value when it must differ from `id`
79
+ - **`disabled`** (optional, default `false`) — non-interactive row
80
+ - **`href`** (optional) — when the `label` slot renders a link, **Enter** delegates to the anchor click
81
+
82
+ ### Events the parent should handle
83
+
84
+ | Event | When it fires | Typical parent action |
85
+ | --- | --- | --- |
86
+ | `update:modelValue` | User selects an enabled option | Persist selection; optionally close overlay (`VJoyDropdown` does this when `closeOnSelect` is true) |
87
+ | `close:dropdownList` | **Escape** or **Tab** while focus is inside the built-in listbox | Close the surrounding overlay and return focus to the trigger |
88
+ | `reset` | User clicks the reset CTA (`reset` prop) | Clear filter / checkbox state in the parent — the list does not mutate models |
89
+
90
+ ---
91
+
92
+ ## Variants
93
+
94
+ `VJoyDropdownList` has **no visual emphasis variants** (no primary/secondary). Appearance is controlled by **size**, **selection state**, and **disabled** rows only.
95
+
96
+ Choose a different component if you need card layouts, native form-field chrome, or trigger-driven overlay behavior.
97
+
98
+ ### Not in the API: `dropdown-list-item` slot
99
+
100
+ `defineSlots` documents a `dropdown-list-item` scoped slot, but the template wires **`label`** for per-row customization. Use `#label="{ …option }"` (or let `VJoyDropdown` forward `dropdown-item`) — do not rely on `dropdown-list-item` until it is implemented.
101
+
102
+ ---
103
+
104
+ ## Sizes
105
+
106
+ `size` controls **row density** (height and font size via form-size tokens). It does not size header/footer padding beyond shared spacing tokens.
107
+
108
+ | Size | Use when |
109
+ | --- | --- |
110
+ | `medium` | Default product menus and option lists |
111
+ | `small` | Dense toolbars, filter bars, tables |
112
+ | `xsmall` | Very compact UI (match `xsmall` triggers on `VJoyButton` / `VJoyIconButton`) |
113
+
114
+ Heights come from `--dropdown-item-height` / `--dropdown-item-font-size` inside the component — **don’t hardcode item heights** in apps.
115
+
116
+ ---
117
+
118
+ ## States
119
+
120
+ | State | What the user sees / can do |
121
+ | --- | --- |
122
+ | Default row | Neutral text; pointer cursor; hover highlights row |
123
+ | Selected row | `joy-dropdown-list__item--selected` — secondary background and text color; `aria-selected="true"` |
124
+ | Focused row | Inset focus ring on the `<li>` (`box-shadow` focus token) |
125
+ | Disabled row | Muted text; `cursor: not-allowed`; not clickable; `aria-disabled="true"` |
126
+ | Scroll overflow | Items area scrolls vertically when content exceeds `height` |
127
+ | Custom slot body | `dropdown-list-items` adds padding class; built-in listbox keyboard patterns are **not** applied |
128
+ | Reset visible | Header shows reset CTA when `reset` is true |
129
+
130
+ On mount, if `modelValue` matches an option, that row receives focus and the scroller scrolls it into view.
131
+
132
+ ---
133
+
134
+ ## Best practices
135
+
136
+ Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
137
+
138
+ ### Selection and data
139
+
140
+ - **MUST** give every option a unique `id`.
141
+ - **MUST** bind `v-model` when a single selected value should persist across opens.
142
+ - **SHOULD** set `value` on an option only when the stored model must differ from `id`.
143
+ - **SHOULD** handle `@reset` when `reset` is true — the component does not clear parent state.
144
+ - **SHOULD** listen for `@close:dropdown-list` when the list sits inside a custom overlay (mirror `VJoyDropdown` behavior).
145
+
146
+ ### Layout
147
+
148
+ - **SHOULD** set `width` to fit the longest expected label (rows truncate with ellipsis; full text is in `title`).
149
+ - **SHOULD** set `height` when option count can exceed the viewport — scrolling is internal to the items area.
150
+ - **MUST NOT** hardcode panel border, radius, shadow, or `z-index` — Joy tokens apply via `.joy-dropdown-list`.
151
+
152
+ ### Composition
153
+
154
+ - **SHOULD** prefer `VJoyDropdown` when you need a trigger, click-outside, open/close animation, and focus return.
155
+ - **SHOULD** use `dropdown-list-items` + `VJoyCheckboxGroup` for multi-select filter panels; keep the list open until the parent closes it.
156
+ - **MUST** set `aria-label` or `aria-labelledby` when the list is not labelled by a visible trigger element (standalone panels).
157
+
158
+ ### Implementation
159
+
160
+ - **MUST** use only documented prop / enum values (MCP / `JoyDropdownList.types.ts`).
161
+ - **MUST** use Joy tokens and slots — no custom list chrome CSS.
162
+ - **MUST NOT** invent props (`variant`, `open`, `direction`, …).
163
+ - **MUST NOT** mount `VJoyDropdownList` without a parent plan for open/close and focus when users expect dropdown behavior — use `VJoyDropdown`.
164
+
165
+ ### Do / don’t
166
+
167
+ | Do | Don’t |
168
+ | --- | --- |
169
+ | `VJoyDropdown` for full dropdown UX | `VJoyDropdownList` alone with no overlay logic |
170
+ | `label` slot for custom row content | Undocumented `dropdown-list-item` slot |
171
+ | `@reset` handler when `reset` is true | Expect reset to clear `v-model` automatically |
172
+ | `aria-labelledby` from trigger id (`VJoyDropdown` pattern) | Orphan listbox with no accessible name |
173
+ | Match list `size` to trigger density | `xsmall` trigger + `medium` rows |
174
+
175
+ ---
176
+
177
+ ## Content
178
+
179
+ Dropdown lists surface copy on options, in the optional header/footer, and on the reset control. Keep labels scannable — users scan the panel to decide quickly.
180
+
181
+ ### Label formula
182
+
183
+ **Options:** **plain noun or short phrase** — e.g. `Paris`, `Export as PDF`, `Last 30 days`. Use parallel grammar across siblings.
184
+
185
+ **Header slot:** **instruction or context** — e.g. `Select a station`, `Filter by status`.
186
+
187
+ **Reset CTA:** **verb + scope** — e.g. `Reset filter`, `Clear selection`. Code falls back to `Reset` when `reset-label` is omitted.
188
+
189
+ **Footer actions:** same rules as `VJoyButton` — infinitive verb + noun (`Apply filters`, `Save changes`).
190
+
191
+ ### Writing rules
192
+
193
+ | Strength | Rule |
194
+ | --- | --- |
195
+ | **MUST** | Option labels stay concise; long text truncates with `title` tooltip on the row |
196
+ | **MUST** | Disabled options include context elsewhere if the reason isn’t obvious |
197
+ | **MUST NOT** | Vague option labels — ❌ `Item 1`, ❌ `Click here` |
198
+ | **MUST NOT** | Duplicate header context on every option when the list topic is clear |
199
+ | **SHOULD** | ≤ 3 words per option where possible |
200
+ | **SHOULD** | Use sentence case for multi-word labels |
201
+ | **SHOULD** | Name destructive actions explicitly — ✅ `Delete project` · ❌ `Delete` alone in a mixed list |
202
+ | **SHOULD** | Localize `reset-label` — do not rely on the English default `Reset` in product UI |
203
+ | **SHOULD** (FR) | Follow French grammar for labels and reset copy when the UI is localized |
204
+
205
+ ### Patterns
206
+
207
+ **Simple pick list** — Options are place names or enum values. No header unless the trigger label is ambiguous.
208
+
209
+ **Filter panel** — Header holds search or title; body uses `dropdown-list-items` with checkboxes; footer holds `Apply filters`; `reset` clears parent filter state.
210
+
211
+ **Link rows** — Use the `label` slot to render `<a href="…">`; **Enter** on a focused row activates the link instead of emitting `update:modelValue`.
212
+
213
+ ---
214
+
215
+ ## Accessibility
216
+
217
+ Behaviors (what must happen), not slogans.
218
+
219
+ | Behavior | Expectation |
220
+ | --- | --- |
221
+ | List naming | Set `aria-label` or `aria-labelledby` on the listbox — `VJoyDropdown` passes the trigger’s generated id via `aria-labelledby` |
222
+ | Role (options mode) | `role="listbox"` on `<ul>`; each row `role="option"` with `aria-selected` / `aria-disabled` |
223
+ | Row name | `aria-label` and `title` use `option.label` |
224
+ | Focus on mount | Selected option receives focus; scroller scrolls it into view |
225
+ | Keyboard (built-in list) | **ArrowUp** / **ArrowDown** move focus; **Home** / **End** jump; **Enter** selects or activates link; **Space** prevented from scrolling page while row focused |
226
+ | Keyboard (close) | **Escape** or **Tab** while focus is inside the listbox emits `close:dropdownList` |
227
+ | Keyboard (custom slot) | When using `dropdown-list-items` without `options`, listbox patterns are not enforced — manage focus inside custom content |
228
+ | Disabled options | `aria-disabled="true"`; not selectable on click |
229
+
230
+ Target: **WCAG 2.2 AA** where the product requires it.
231
+
232
+ ---
233
+
234
+ ## Examples
235
+
236
+ ```vue
237
+ <script setup lang="ts">
238
+ import { ref } from 'vue'
239
+ import { VJoyDropdownList, VJoyButton } from '@maltjoy/core-vue'
240
+
241
+ interface StationOption {
242
+ id: string
243
+ label: string
244
+ value?: string
245
+ disabled?: boolean
246
+ }
247
+
248
+ const station = ref('toulouse')
249
+ const open = ref(false)
250
+
251
+ const stations: StationOption[] = [
252
+ { id: 'paris', label: 'Paris Montparnasse' },
253
+ { id: 'toulouse', label: 'Toulouse Matabiau' },
254
+ { id: 'bordeaux', label: 'Bordeaux Saint-Jean' },
255
+ { id: 'marseille', label: 'Marseille Saint-Charles', disabled: true },
256
+ ]
257
+
258
+ function clearFilter() {
259
+ station.value = ''
260
+ }
261
+ </script>
262
+
263
+ <template>
264
+ <!-- Built-in options + v-model -->
265
+ <VJoyDropdownList
266
+ v-model="station"
267
+ :options="stations"
268
+ aria-label="French stations"
269
+ width="300"
270
+ height="200"
271
+ size="medium"
272
+ />
273
+
274
+ <!-- Header, reset, and footer -->
275
+ <VJoyDropdownList
276
+ v-model="station"
277
+ :options="stations"
278
+ reset
279
+ reset-label="Clear selection"
280
+ width="280"
281
+ height="240"
282
+ aria-labelledby="station-picker-label"
283
+ @reset="clearFilter"
284
+ >
285
+ <template #dropdown-list-header>
286
+ <span id="station-picker-label">Choose a station</span>
287
+ </template>
288
+ <template #dropdown-list-footer>
289
+ <VJoyButton size="xxsmall" variant="main">Done</VJoyButton>
290
+ </template>
291
+ </VJoyDropdownList>
292
+
293
+ <!-- Custom body (no built-in keyboard listbox) -->
294
+ <VJoyDropdownList width="240" height="200" aria-label="Filter options">
295
+ <template #dropdown-list-items>
296
+ <!-- VJoyCheckboxGroup or other controls -->
297
+ </template>
298
+ </VJoyDropdownList>
299
+
300
+ <!-- Custom row label -->
301
+ <VJoyDropdownList v-model="station" :options="stations" aria-label="Stations">
302
+ <template #label="{ label, href }">
303
+ <a v-if="href" :href="href">{{ label }}</a>
304
+ <span v-else>{{ label }}</span>
305
+ </template>
306
+ </VJoyDropdownList>
307
+ </template>
308
+ ```
309
+
310
+ In Nuxt (or apps with auto-import), omit the import if `VJoyDropdownList` is already global.
311
+
312
+ For trigger, positioning, and click-outside, wrap usage in `VJoyDropdown` instead of reimplementing those behaviors around the list alone.
313
+
314
+ ---
315
+
316
+ ## Related
317
+
318
+ | Component | Use when |
319
+ | --- | --- |
320
+ | `VJoyDropdown` | Full dropdown with trigger, overlay lifecycle, and focus management |
321
+ | `VJoySelect` | Labeled single-select form field with native `<select>` semantics |
322
+ | `VJoyCheckboxGroup` | Multi-select content inside `dropdown-list-items` |
323
+ | `VJoyFilterBar` | Composed filter UI that embeds dropdown patterns |
324
+ | `VJoyButton` | Footer actions and standalone triggers when not using `VJoyDropdown` |
325
+ | `VJoyMenu` | Persistent navigation menus with `VJoyMenuItem` |
326
+
327
+ ---
328
+
329
+ ## For agents
330
+
331
+ Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoyDropdownList.dsds.json`](./JoyDropdownList.dsds.json).
332
+
333
+ ### Pick the right component
334
+
335
+ | If the intent is… | Emit… | Do not emit… |
336
+ | --- | --- | --- |
337
+ | Overflow / contextual menu from a trigger | `VJoyDropdown` | `VJoyDropdownList` without overlay parent |
338
+ | List panel only (parent owns open/close) | `VJoyDropdownList` | `VJoyDropdown` with empty `dropdown-button` |
339
+ | Labeled form single-select | `VJoySelect` | `VJoyDropdownList` mimicking a native select |
340
+ | Multi-select filters with checkboxes | `VJoyDropdown` or `VJoyDropdownList` in custom overlay + `dropdown-list-items` | `options` array with fake checkbox markup |
341
+ | Custom row content per option | `#label` scoped slot | `dropdown-list-item` slot (not wired) |
342
+
343
+ ### Hard rules
344
+
345
+ 1. **MUST** call `joy_get_component_info("VJoyDropdownList")` and/or read `JoyDropdownList.types.ts` before generating props.
346
+ 2. **MUST** give every `options` entry a unique `id` and a `label`.
347
+ 3. **MUST** bind `v-model` when single selection from `options` should persist.
348
+ 4. **MUST** set `aria-label` or `aria-labelledby` when no visible trigger labels the list.
349
+ 5. **MUST** handle `@reset` when `reset` is true — reset does not mutate parent state.
350
+ 6. **MUST** handle `@close:dropdown-list` when embedding in a custom overlay (close on Escape / Tab from list focus).
351
+ 7. **MUST NOT** invent props (`variant`, `direction`, `justify`, `open`, …) or size values outside `xsmall` | `small` | `medium`.
352
+ 8. **MUST NOT** override `.joy-dropdown-list` border, shadow, radius, or item heights in product CSS.
353
+ 9. **SHOULD** use `VJoyDropdown` instead of standalone list when trigger + click-outside are needed.
354
+ 10. **SHOULD** pass `width` / `height` as strings (pixel values without units in the prop value, e.g. `width="300"`).
355
+
356
+ ### Slot map
357
+
358
+ | Slot | Typical content |
359
+ | --- | --- |
360
+ | `dropdown-list-header` | Title, search field, instructions |
361
+ | `dropdown-list-items` | `VJoyCheckboxGroup`, custom markup (replaces built-in `options` list) |
362
+ | `label` | Custom per-row content (text, links) |
363
+ | `dropdown-list-footer` | `VJoyButton` Apply / Done |
364
+
365
+ ### Checklist
366
+
367
+ - [ ] Correct component (`VJoyDropdownList` or named alternative)
368
+ - [ ] Props / enums from MCP or types only
369
+ - [ ] `options` rows have unique `id` + `label`
370
+ - [ ] `v-model` bound when selection should persist
371
+ - [ ] `aria-label` or `aria-labelledby` set for standalone list
372
+ - [ ] `@reset` handler when `reset` is true
373
+ - [ ] `@close:dropdown-list` wired when inside custom overlay
374
+ - [ ] `#label` slot for row customization (not `dropdown-list-item`)
375
+ - [ ] No invented props / no custom list chrome CSS
376
+ - [ ] `size` matches surrounding trigger density
377
+
378
+ ---
379
+
380
+ ## DSDS mapping
381
+
382
+ This Markdown mirrors [`JoyDropdownList.dsds.json`](./JoyDropdownList.dsds.json) (DSDS **0.15.2**).
383
+
384
+ | Section here | DSDS block |
385
+ | --- | --- |
386
+ | When to use / not | `use-cases` |
387
+ | Anatomy | `anatomy` |
388
+ | Sizes | `variants` |
389
+ | States | `states` |
390
+ | Best practices / content rules | `guidelines` (+ `content`) |
391
+ | Accessibility | `accessibility` |
392
+ | Examples | `sections` |
393
+ | For agents | `agentDocumentBlocks` |
394
+ | Props detail | `api` in JSON + Joy MCP |
395
+ | Imports | `imports` in JSON |
396
+ | Token sizing | `design-specifications` in JSON (`--dropdown-item-height`, form-size tokens) |
@@ -70,7 +70,7 @@
70
70
  "type": "string",
71
71
  "declarations": [
72
72
  {
73
- "file": "/home/kevincarnaille/dev/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
73
+ "file": "/Users/pierrereynaud/Documents/Work/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
74
74
  "range": [
75
75
  2376,
76
76
  2387
@@ -88,7 +88,7 @@
88
88
  "type": "string | undefined",
89
89
  "declarations": [
90
90
  {
91
- "file": "/home/kevincarnaille/dev/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
91
+ "file": "/Users/pierrereynaud/Documents/Work/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
92
92
  "range": [
93
93
  2396,
94
94
  2411
@@ -113,7 +113,7 @@
113
113
  "type": "string",
114
114
  "declarations": [
115
115
  {
116
- "file": "/home/kevincarnaille/dev/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
116
+ "file": "/Users/pierrereynaud/Documents/Work/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
117
117
  "range": [
118
118
  2420,
119
119
  2434
@@ -131,7 +131,7 @@
131
131
  "type": "string | undefined",
132
132
  "declarations": [
133
133
  {
134
- "file": "/home/kevincarnaille/dev/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
134
+ "file": "/Users/pierrereynaud/Documents/Work/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
135
135
  "range": [
136
136
  2443,
137
137
  2457
@@ -154,7 +154,7 @@
154
154
  "type": "boolean | undefined",
155
155
  "declarations": [
156
156
  {
157
- "file": "/home/kevincarnaille/dev/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
157
+ "file": "/Users/pierrereynaud/Documents/Work/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
158
158
  "range": [
159
159
  2516,
160
160
  2535
@@ -185,7 +185,7 @@
185
185
  "type": "boolean | undefined",
186
186
  "declarations": [
187
187
  {
188
- "file": "/home/kevincarnaille/dev/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
188
+ "file": "/Users/pierrereynaud/Documents/Work/design-system/packages/core-vue/src/components/JoyDropdownList/VJoyDropdownList.vue",
189
189
  "range": [
190
190
  2594,
191
191
  2612
@@ -265,7 +265,7 @@
265
265
  },
266
266
  {
267
267
  "name": "dropdown-list-items",
268
- "description": "Free slot in order to render any kind of component. Can be handy to use it with VJoyMultiCheckbox for instance."
268
+ "description": "Free slot in order to render any kind of component. Can be handy to use it with VJoyCheckboxGroup for instance."
269
269
  },
270
270
  {
271
271
  "name": "dropdown-list-item",