@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,172 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: VJoyScreenLoader
|
|
3
|
+
status: draft
|
|
4
|
+
language: en
|
|
5
|
+
dsds: JoyScreenLoader.dsds.json
|
|
6
|
+
dsdsVersion: "0.15.2"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Screen loader
|
|
10
|
+
|
|
11
|
+
Use **`VJoyScreenLoader`** when a page, panel, or section is loading and you need to block interaction while showing a centered spinner and optional status message.
|
|
12
|
+
|
|
13
|
+
Set `overlay` to cover the parent container absolutely (typical inside `VJoyPanel` or `VJoyTemplate`). Choose `color` to match the surface behind the loader — `light` on pale backgrounds, `dark` on imagery or dark overlays.
|
|
14
|
+
|
|
15
|
+
| | |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| **Code** | `VJoyScreenLoader` from `@maltjoy/core-vue` |
|
|
18
|
+
| **Design** | [Figma — Joy Core Design System](https://www.figma.com/design/jfndp2QBJGS9yeuWbeGuvD/) |
|
|
19
|
+
| **Storybook** | [VJoyScreenLoader docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-loader-vjoyscreenloader--docs) |
|
|
20
|
+
| **DSDS (machine)** | [`JoyScreenLoader.dsds.json`](./JoyScreenLoader.dsds.json) |
|
|
21
|
+
| **Live API** | `joy_list_components` (Loader) · types in `VJoyScreenLoader.types.ts` |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## When to use this component
|
|
26
|
+
|
|
27
|
+
Use a screen loader when content is not ready yet and the user should not interact with the underlying UI — for example fetching panel data, switching template routes, or waiting for a large section to render.
|
|
28
|
+
|
|
29
|
+
Mount it over a **positioned parent** (`position: relative` on the container) with `overlay` so it fills that region. Add a short message in the `content` slot when the wait may exceed a second or two.
|
|
30
|
+
|
|
31
|
+
Prefer the built-in `loading` prop on **`VJoyPanel`** and **`VJoyTemplate`** when the loader belongs to those layouts — they wire `VJoyScreenLoader` with `overlay`, blur the content behind, and expose `panel-loader-content` / `template-loader-content` slots.
|
|
32
|
+
|
|
33
|
+
### When not to use this component
|
|
34
|
+
|
|
35
|
+
| Need | Use instead |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| Inline loading on a single button or form control | `VJoyButton` `loading` prop or `VJoySpinner` |
|
|
38
|
+
| Skeleton placeholder while layout is known | `VJoyBlockSkeleton`, `VJoyFormFieldSkeleton` |
|
|
39
|
+
| Full-page app bootstrap before Vue mounts | App-level splash / router guard pattern — not this component alone |
|
|
40
|
+
| Progress with a known completion ratio | `VJoyProgressBar` |
|
|
41
|
+
| Custom full-screen overlay with ad-hoc CSS | Still use `VJoyScreenLoader` + Joy tokens — don’t reinvent loader chrome |
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## How it works
|
|
46
|
+
|
|
47
|
+
### Anatomy
|
|
48
|
+
|
|
49
|
+
| Part | Required? | What it is |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| Root container | Yes | Flexbox centering wrapper (`joy-screen-loader`, `joy-screen-loader--{color}`) |
|
|
52
|
+
| Overlay layer | No | `joy-screen-loader--overlay` when `overlay` is true — `position: absolute`, fills parent |
|
|
53
|
+
| Spinner area | Yes | Default `VJoySpinner` (`teal` on `light`, `white` on `dark`); override via `spinner` slot |
|
|
54
|
+
| Content area | No | `content` slot — centered text or markup below the spinner |
|
|
55
|
+
| Scrim background | Yes | Semi-transparent `light` (`rgba(255, 255, 255, 0.8)`) or `dark` (`var(--joy-color-overlay)`) |
|
|
56
|
+
|
|
57
|
+
### Default values
|
|
58
|
+
|
|
59
|
+
- `color` defaults to **`light`**
|
|
60
|
+
- `overlay` defaults to **`false`**
|
|
61
|
+
|
|
62
|
+
Props and slots: use Storybook, `VJoyScreenLoader.vue`, or `VJoyScreenLoader.types.ts` — this page focuses on **when** and **how**, not a full API dump.
|
|
63
|
+
|
|
64
|
+
### Color modes
|
|
65
|
+
|
|
66
|
+
| `color` | Background | Default spinner |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| `light` | White scrim (80% opacity) | `VJoySpinner` `teal` |
|
|
69
|
+
| `dark` | `--joy-color-overlay` | `VJoySpinner` `white` |
|
|
70
|
+
|
|
71
|
+
Allowed values: `TJoyScreenLoaderColors` — **`light`** \| **`dark`** only.
|
|
72
|
+
|
|
73
|
+
### Layout tokens
|
|
74
|
+
|
|
75
|
+
Padding uses `--screen-loader-padding` (default `var(--joy-core-spacing-4)`). Parent containers may set `--screen-loader-min-height` when the loader should fill a minimum area without `overlay`.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Best practices
|
|
80
|
+
|
|
81
|
+
Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
|
|
82
|
+
|
|
83
|
+
### When to show
|
|
84
|
+
|
|
85
|
+
- **SHOULD** use a screen loader when the entire panel or template content is unavailable — not for partial inline updates.
|
|
86
|
+
- **SHOULD** pair `overlay` with a parent that has `position: relative` so coverage is predictable.
|
|
87
|
+
- **SHOULD** add a short `content` message when loading may take more than ~1 second.
|
|
88
|
+
- **MUST NOT** leave the loader visible after data has loaded — tie visibility to a boolean loading flag.
|
|
89
|
+
|
|
90
|
+
### Implementation
|
|
91
|
+
|
|
92
|
+
- **MUST** use only `color` values from `TJoyScreenLoaderColors` (`light`, `dark`).
|
|
93
|
+
- **MUST** use Joy tokens — no custom `.joy-screen-loader` background or z-index overrides in product CSS.
|
|
94
|
+
- **MUST NOT** invent props (`size`, `variant`, `message`, …).
|
|
95
|
+
- **SHOULD** prefer `VJoyPanel` / `VJoyTemplate` `loading` props instead of duplicating loader wiring.
|
|
96
|
+
- **SHOULD** keep the default `VJoySpinner` unless design requires a custom animation via the `spinner` slot.
|
|
97
|
+
|
|
98
|
+
### Do / don’t
|
|
99
|
+
|
|
100
|
+
| Do | Don’t |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `overlay` inside a positioned panel or template | Loader without a sized/positioned parent |
|
|
103
|
+
| `color="dark"` on busy or photographic backgrounds | `light` scrim on dark imagery (poor contrast) |
|
|
104
|
+
| Short status copy in `content` | Long paragraphs or error details in the loader |
|
|
105
|
+
| `VJoyPanel` `loading` for panel fetches | Manual duplicate loader + blur logic |
|
|
106
|
+
| Hide loader when `loading` becomes false | Indefinite spinner with no feedback |
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Accessibility
|
|
111
|
+
|
|
112
|
+
Behaviors (what must happen), not slogans.
|
|
113
|
+
|
|
114
|
+
| Behavior | Expectation |
|
|
115
|
+
| --- | --- |
|
|
116
|
+
| Role | No dedicated ARIA role on the component — treat as a **status** region in the parent when loading blocks the UI |
|
|
117
|
+
| Name | If `content` slot text is shown, it should describe what is loading (`Loading projects…`) — not generic `Please wait` alone |
|
|
118
|
+
| Keyboard | Underlying controls should be **unreachable** while overlay is active (overlay covers the parent); ensure focus does not land on blurred content behind |
|
|
119
|
+
| Focus | When loading ends, return focus to a sensible control in the newly loaded content if focus was trapped in the loading region |
|
|
120
|
+
| Motion | Spinner uses CSS animation via `VJoySpinner`; respect `prefers-reduced-motion` at app level where implemented |
|
|
121
|
+
| Timing | Avoid flashing the loader for sub-300ms requests — debounce or minimum display time at app level if flicker is noticeable |
|
|
122
|
+
|
|
123
|
+
Target: **WCAG 2.2 AA** where the product requires it.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Examples
|
|
128
|
+
|
|
129
|
+
```vue
|
|
130
|
+
<script setup lang="ts">
|
|
131
|
+
import { ref } from 'vue'
|
|
132
|
+
import { VJoyScreenLoader, VJoyPanel } from '@maltjoy/core-vue'
|
|
133
|
+
|
|
134
|
+
const loading = ref(true)
|
|
135
|
+
</script>
|
|
136
|
+
|
|
137
|
+
<template>
|
|
138
|
+
<!-- Standalone section loader -->
|
|
139
|
+
<div style="position: relative; min-height: 200px">
|
|
140
|
+
<VJoyScreenLoader v-if="loading" overlay>
|
|
141
|
+
<template #content>Loading projects…</template>
|
|
142
|
+
</VJoyScreenLoader>
|
|
143
|
+
<!-- section content -->
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<!-- Panel-integrated (preferred) -->
|
|
147
|
+
<VJoyPanel :loading="loading" loading-color="light">
|
|
148
|
+
<template #panel-body>…</template>
|
|
149
|
+
<template #panel-loader-content>Fetching latest data…</template>
|
|
150
|
+
</VJoyPanel>
|
|
151
|
+
|
|
152
|
+
<!-- Dark overlay on imagery -->
|
|
153
|
+
<VJoyScreenLoader color="dark" overlay>
|
|
154
|
+
<template #content>Preparing your workspace…</template>
|
|
155
|
+
</VJoyScreenLoader>
|
|
156
|
+
</template>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
In Nuxt (or apps with auto-import), omit the import if `VJoyScreenLoader` is already global.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Related
|
|
164
|
+
|
|
165
|
+
| Component | Use when |
|
|
166
|
+
| --- | --- |
|
|
167
|
+
| `VJoySpinner` | Small inline loading indicator (buttons, fields) — default inside this loader |
|
|
168
|
+
| `VJoyPanel` | Card/section layout with built-in `loading` + `panel-loader-content` |
|
|
169
|
+
| `VJoyTemplate` | Full page layout with built-in `loading` + `template-loader-content` |
|
|
170
|
+
| `VJoyBlockSkeleton` | Placeholder blocks when structure is known before data arrives |
|
|
171
|
+
| `VJoyFormFieldSkeleton` | Skeleton rows for form-heavy loading states |
|
|
172
|
+
| `VJoyProgressBar` | Determinate progress (upload %, step completion) |
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: VJoySelect
|
|
3
|
+
status: draft
|
|
4
|
+
language: en
|
|
5
|
+
dsds: JoySelect.dsds.json
|
|
6
|
+
dsdsVersion: "0.15.2"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Select
|
|
10
|
+
|
|
11
|
+
Use **`VJoySelect`** when the user must pick **one** option from a predefined list in a form — country, role, status, or any bounded enumeration.
|
|
12
|
+
|
|
13
|
+
It wraps a native `<select>` with Joy form-field styling, optional integrated labeling, size variants, and validation states. Options are native `<option>` elements in the default slot (including `v-for`). Bind the value with `v-model`.
|
|
14
|
+
|
|
15
|
+
| | |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| **Code** | `VJoySelect` from `@maltjoy/core-vue` |
|
|
18
|
+
| **Design** | [Figma — Joy components](https://www.figma.com/design/jfndp2QBJGS9yeuWbeGuvD/) |
|
|
19
|
+
| **Storybook** | [VJoySelect docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-forms-vjoyselect--docs) |
|
|
20
|
+
| **DSDS (machine)** | [`JoySelect.dsds.json`](./JoySelect.dsds.json) |
|
|
21
|
+
| **Live API** | `joy_get_component_info("VJoySelect")` |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## When to use this component
|
|
26
|
+
|
|
27
|
+
Use a select when the user chooses a single value from a list that is known upfront and does not need rich per-option layout — for example picking a country, currency, contract type, or workflow status.
|
|
28
|
+
|
|
29
|
+
Selects work well with **roughly 4–20** options (fewer is fine). They save vertical space compared to radio groups and use familiar native dropdown behavior on every platform.
|
|
30
|
+
|
|
31
|
+
### When not to use this component
|
|
32
|
+
|
|
33
|
+
| Need | Use instead |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| Every option should stay visible (2–7 parallel choices) | `VJoyRadio` inside `VJoyRadioGroup` |
|
|
36
|
+
| Multiple selections from a list | `VJoyCheckbox` inside `VJoyCheckboxGroup` |
|
|
37
|
+
| Card-style selectable tiles | `VJoySelectableItemGroup` |
|
|
38
|
+
| Contextual menu or overflow actions from a custom trigger | `VJoyDropdown` |
|
|
39
|
+
| Free-text entry or search-as-you-type | `VJoyInput` (or a combobox pattern if the product provides one) |
|
|
40
|
+
| Custom-styled native `<select>` | Still use `VJoySelect` + Joy tokens — don’t reinvent chrome |
|
|
41
|
+
|
|
42
|
+
> **Deprecated:** the `select-label` slot is deprecated. Prefer the `label` prop for simple text, or place `VJoyLabel` **above** the select with matching `for` / `id` (see [With external label](#with-external-label)).
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## How it works
|
|
47
|
+
|
|
48
|
+
### Anatomy
|
|
49
|
+
|
|
50
|
+
| Part | Required? | What it is |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| Label | No (but **SHOULD** in forms) | Built-in `VJoyLabel` when `label` is set, or external `VJoyLabel` / `select-label` slot |
|
|
53
|
+
| Select wrapper | Yes | Styled container (`.joy-select__wrapper`) with size and state classes |
|
|
54
|
+
| Native `<select>` | Yes | Focusable control with `data-joy-form-field`; inherits undeclared native attributes |
|
|
55
|
+
| Options (`default` slot) | Yes | Native `<option>` elements (supports `v-for`) |
|
|
56
|
+
| Chevron icon | Yes | Decorative `chevron-down` — pointer events disabled |
|
|
57
|
+
| Focus ring | Yes | Focus styling on the native select — don’t remove it in product CSS |
|
|
58
|
+
|
|
59
|
+
### Default values
|
|
60
|
+
|
|
61
|
+
- `size` defaults to **`medium`**
|
|
62
|
+
- `modelValue` defaults to **`""`**
|
|
63
|
+
- `disabled` defaults to **`false`**
|
|
64
|
+
- `invalid` defaults to **`false`**
|
|
65
|
+
- `required` defaults to **`false`**
|
|
66
|
+
- `requiredMark` defaults to **`false`**
|
|
67
|
+
- `labelSize` — when omitted, **`medium`** select uses label size `medium`; **`small`** / **`xsmall`** selects use label size `small`
|
|
68
|
+
|
|
69
|
+
Props, events, and slots: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
|
|
70
|
+
|
|
71
|
+
### Label binding
|
|
72
|
+
|
|
73
|
+
- **Simple:** set `label` — the component renders `VJoyLabel` linked via `for` to `id` or `name`.
|
|
74
|
+
- **Flexible:** omit `label` and render `VJoyLabel` above the select; **`for` on the label MUST match `id` or `name` on the select**.
|
|
75
|
+
- **No visible label:** set `ariaLabel` only when `label` is undefined (e.g. compact toolbar filters).
|
|
76
|
+
|
|
77
|
+
### Options and model
|
|
78
|
+
|
|
79
|
+
- **MUST** provide options as native `<option>` tags in the default slot.
|
|
80
|
+
- **SHOULD** include a disabled placeholder option (`value=""`) when no value is pre-selected.
|
|
81
|
+
- `v-model` binds `modelValue`; changes emit `update:modelValue` with a **string** (native select value).
|
|
82
|
+
- `v-for` on options is supported — the component flattens option VNodes from the slot.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Variants
|
|
87
|
+
|
|
88
|
+
`VJoySelect` has **no visual emphasis variants** (no primary/secondary). Appearance is controlled by **size** and **state** (`disabled`, `invalid`, focus) only.
|
|
89
|
+
|
|
90
|
+
Choose a different component if you need card layouts, custom option rows, or a non-native overlay list.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Sizes
|
|
95
|
+
|
|
96
|
+
Choose size for **density**, not decoration. Heights come from `--joy-form-field-height-*` inside the component — **don’t hardcode pixel heights** in apps.
|
|
97
|
+
|
|
98
|
+
| Size | Use when |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `medium` | Default product forms and settings |
|
|
101
|
+
| `small` | Dense forms, side panels, or secondary filters |
|
|
102
|
+
| `xsmall` | Compact toolbars, table inline filters, very tight layouts |
|
|
103
|
+
|
|
104
|
+
Typography scales with size via Joy font tokens (`--joy-font-size-primary-*`). The chevron uses a smaller icon token on `xsmall`.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## States
|
|
109
|
+
|
|
110
|
+
| State | What the user sees / can do |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| Default | Enabled; shows current selection or placeholder |
|
|
113
|
+
| Hover | Border accent on pointer devices (when not disabled) |
|
|
114
|
+
| Focus | Border and text use focus color; `.joy-select--focusing` on wrapper |
|
|
115
|
+
| Disabled | Muted field; `not-allowed` cursor; not submittable — **explain why** nearby when the reason isn’t obvious |
|
|
116
|
+
| Invalid | Error border and chevron color; `aria-invalid="true"` — pair with `VJoyFormError` or helper text |
|
|
117
|
+
| Required | Native `required` on the select; optional asterisk via `required` + `requiredMark` on the label |
|
|
118
|
+
|
|
119
|
+
When validation fails, set `invalid` and show a `VJoyFormError` (or equivalent) explaining what to fix. Don’t rely on color alone.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Best practices
|
|
124
|
+
|
|
125
|
+
Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
|
|
126
|
+
|
|
127
|
+
### Selection and hierarchy
|
|
128
|
+
|
|
129
|
+
- **SHOULD** use `VJoySelect` when there are **roughly 4+** options or vertical space is limited.
|
|
130
|
+
- **SHOULD** pre-select a sensible default when one option is clearly recommended (omit the placeholder).
|
|
131
|
+
- **SHOULD** keep option labels **parallel** (same grammatical structure and similar length).
|
|
132
|
+
- **MUST NOT** use a select for yes/no when two radios would be clearer — unless space is critically tight.
|
|
133
|
+
- **MUST NOT** use a select for actions (Save, Delete) — use `VJoyButton` or `VJoyDropdown`.
|
|
134
|
+
|
|
135
|
+
### Implementation
|
|
136
|
+
|
|
137
|
+
- **MUST** use only `size` values from MCP or `JoySelect.types.ts` (`xsmall`, `small`, `medium`).
|
|
138
|
+
- **MUST** set `id` or `name` on every select in a form (unique per field).
|
|
139
|
+
- **MUST** style with Joy tokens — no custom `.joy-select` chrome overrides.
|
|
140
|
+
- **MUST NOT** invent props (`variant`, `color`, `placeholder`, …).
|
|
141
|
+
- **MUST NOT** replace the native `<select>` with a custom div list for standard form fields.
|
|
142
|
+
- Native attributes not declared as Vue props (e.g. `autocomplete`, `form`) are forwarded to the underlying `<select>`.
|
|
143
|
+
|
|
144
|
+
### Labeling
|
|
145
|
+
|
|
146
|
+
- **MUST** provide an accessible name: `label` prop, external `VJoyLabel` with correct `for`, or `ariaLabel` when no visible label.
|
|
147
|
+
- **MUST** match `VJoyLabel` `for` to the select’s `id` (or `name` when `id` is omitted).
|
|
148
|
+
- **SHOULD** use `optionalLabel` for optional fields instead of spelling “(optional)” in the main label.
|
|
149
|
+
- **SHOULD** use `required` + `requiredMark` for required fields in line with other Joy form fields.
|
|
150
|
+
|
|
151
|
+
### Do / don’t
|
|
152
|
+
|
|
153
|
+
| Do | Don’t |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| Native `<option>` in the default slot | Custom divs pretending to be options |
|
|
156
|
+
| Disabled placeholder for “no selection yet” | First real option selected by default with no hint |
|
|
157
|
+
| `VJoyFormError` below invalid selects | Error color with no message |
|
|
158
|
+
| `VJoyLabel` + `for` / `id` pairing | Mismatched or missing label association |
|
|
159
|
+
| `size` + Joy tokens | Hardcoded `32` / `40` / `48`px heights |
|
|
160
|
+
| `VJoyRadioGroup` for 3 visible choices | Select hiding 3 obvious options |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Content
|
|
165
|
+
|
|
166
|
+
Select copy should make the field purpose obvious and keep options scannable in a native dropdown.
|
|
167
|
+
|
|
168
|
+
### Label formula
|
|
169
|
+
|
|
170
|
+
**Noun phrase** naming what is being chosen (not the action):
|
|
171
|
+
|
|
172
|
+
- ✅ Country
|
|
173
|
+
- ✅ Payment method
|
|
174
|
+
- ✅ Contract status
|
|
175
|
+
|
|
176
|
+
Avoid imperative labels on the field itself — ✅ `Billing country` · ❌ `Select your country`.
|
|
177
|
+
|
|
178
|
+
### Placeholder option
|
|
179
|
+
|
|
180
|
+
Use a **disabled** first option with an empty `value` when nothing is selected yet:
|
|
181
|
+
|
|
182
|
+
- ✅ `Select a value`
|
|
183
|
+
- ✅ `Choose an option`
|
|
184
|
+
- ❌ `---` or blank with no instruction
|
|
185
|
+
|
|
186
|
+
Keep placeholder text short and neutral; localize it.
|
|
187
|
+
|
|
188
|
+
### Option labels
|
|
189
|
+
|
|
190
|
+
- **MUST** label each `<option>` with the human-readable choice (`France`, `Pending review`).
|
|
191
|
+
- **MUST** use stable programmatic `value` attributes (codes, IDs) distinct from display text when needed.
|
|
192
|
+
- **SHOULD** use parallel phrasing across options (all countries, all statuses, all “Noun” labels).
|
|
193
|
+
- **MUST NOT** embed instructions or marketing copy inside option text — put that in helper text below the field.
|
|
194
|
+
- **SHOULD** sort options in a predictable order (alphabetical, chronological, or product-defined priority).
|
|
195
|
+
|
|
196
|
+
### Writing rules
|
|
197
|
+
|
|
198
|
+
| Strength | Rule |
|
|
199
|
+
| --- | --- |
|
|
200
|
+
| **MUST** | Field label ≤ ~40 characters; move long guidance to helper text or `VJoyFormError` |
|
|
201
|
+
| **MUST** | Sentence case for labels and options |
|
|
202
|
+
| **MUST** | `optionalLabel` for optional fields (e.g. `Optional`) — not mixed into the primary label |
|
|
203
|
+
| **MUST NOT** | Duplicate the label text as the placeholder (`Country` + placeholder `Country`) |
|
|
204
|
+
| **MUST NOT** | Use “Select …” as the field label — name the **thing** (`Country`, not `Select country`) |
|
|
205
|
+
| **SHOULD** | Option text ≤ ~60 characters; abbreviate consistently if truncation is unavoidable on mobile |
|
|
206
|
+
| **SHOULD** | Group long lists logically (prefer separate fields or product-side optgroups only if native `<optgroup>` is added to the slot) |
|
|
207
|
+
| **SHOULD** (FR) | Use articles when grammar requires them in labels and options |
|
|
208
|
+
|
|
209
|
+
### Patterns
|
|
210
|
+
|
|
211
|
+
**Required field** — `label` + `required` + `requiredMark`; validation message via `VJoyFormError`: ✅ `Country` + `Select a country to continue`.
|
|
212
|
+
|
|
213
|
+
**Optional filter** — `optionalLabel="Optional"` on a filter select in a dense toolbar; consider `size="xsmall"` and `ariaLabel` if the layout has no visible label.
|
|
214
|
+
|
|
215
|
+
**Pre-selected default** — omit the placeholder option when a valid default exists (e.g. user’s current locale).
|
|
216
|
+
|
|
217
|
+
**Invalid state** — `invalid` on the select + error list: ✅ `Payment method` + `Choose a payment method before continuing`.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Accessibility
|
|
222
|
+
|
|
223
|
+
Behaviors (what must happen), not slogans.
|
|
224
|
+
|
|
225
|
+
| Behavior | Expectation |
|
|
226
|
+
| --- | --- |
|
|
227
|
+
| Role | Native `<select>` — never a clickable `<div>` for standard form selection |
|
|
228
|
+
| Name | From associated `VJoyLabel` (`label` prop or external label with `for`/`id`), or `aria-label` when no visible label |
|
|
229
|
+
| Keyboard | **Tab** to focus; **Space** / **Arrow keys** open and move within the list (platform-native) |
|
|
230
|
+
| Focus | Visible focus styling on the select; wrapper gets `.joy-select--focusing` — don’t remove focus rings in product CSS |
|
|
231
|
+
| Disabled | `disabled` on the select; don’t rely on color alone — explain when needed |
|
|
232
|
+
| Invalid | `aria-invalid="true"` when `invalid`; pair with visible error text (`VJoyFormError`) |
|
|
233
|
+
| Required | Native `required` when the field is mandatory; required mark on label is visual supplement |
|
|
234
|
+
| Chevron | Decorative; meaning comes from the labeled select |
|
|
235
|
+
|
|
236
|
+
Target: **WCAG 2.2 AA** where the product requires it.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Examples
|
|
241
|
+
|
|
242
|
+
```vue
|
|
243
|
+
<script setup lang="ts">
|
|
244
|
+
import { ref } from 'vue'
|
|
245
|
+
import { VJoySelect, VJoyLabel, VJoyFormError } from '@maltjoy/core-vue'
|
|
246
|
+
|
|
247
|
+
const country = ref('')
|
|
248
|
+
const invalid = ref(false)
|
|
249
|
+
</script>
|
|
250
|
+
|
|
251
|
+
<template>
|
|
252
|
+
<!-- Happy path — integrated label -->
|
|
253
|
+
<VJoySelect
|
|
254
|
+
v-model="country"
|
|
255
|
+
label="Country"
|
|
256
|
+
name="country"
|
|
257
|
+
required
|
|
258
|
+
required-mark
|
|
259
|
+
>
|
|
260
|
+
<option disabled value="">Select a value</option>
|
|
261
|
+
<option value="fr">France</option>
|
|
262
|
+
<option value="de">Germany</option>
|
|
263
|
+
<option value="es">Spain</option>
|
|
264
|
+
</VJoySelect>
|
|
265
|
+
|
|
266
|
+
<!-- Invalid + error message -->
|
|
267
|
+
<VJoySelect
|
|
268
|
+
v-model="country"
|
|
269
|
+
label="Country"
|
|
270
|
+
name="country"
|
|
271
|
+
:invalid="invalid && !country"
|
|
272
|
+
>
|
|
273
|
+
<option disabled value="">Select a value</option>
|
|
274
|
+
<option value="fr">France</option>
|
|
275
|
+
<option value="de">Germany</option>
|
|
276
|
+
</VJoySelect>
|
|
277
|
+
<VJoyFormError
|
|
278
|
+
v-if="invalid && !country"
|
|
279
|
+
:errors="['Select a country to continue']"
|
|
280
|
+
/>
|
|
281
|
+
|
|
282
|
+
<!-- Dense size -->
|
|
283
|
+
<VJoySelect v-model="country" label="Status" name="status" size="small">
|
|
284
|
+
<option disabled value="">Choose an option</option>
|
|
285
|
+
<option value="active">Active</option>
|
|
286
|
+
<option value="paused">Paused</option>
|
|
287
|
+
</VJoySelect>
|
|
288
|
+
|
|
289
|
+
<!-- Options from data -->
|
|
290
|
+
<VJoySelect v-model="country" label="Country" name="country">
|
|
291
|
+
<option disabled value="">Select a value</option>
|
|
292
|
+
<option v-for="c in ['France', 'Germany', 'Spain']" :key="c" :value="c">
|
|
293
|
+
{{ c }}
|
|
294
|
+
</option>
|
|
295
|
+
</VJoySelect>
|
|
296
|
+
</template>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### With external label
|
|
300
|
+
|
|
301
|
+
```vue
|
|
302
|
+
<script setup lang="ts">
|
|
303
|
+
import { ref } from 'vue'
|
|
304
|
+
import { VJoySelect, VJoyLabel } from '@maltjoy/core-vue'
|
|
305
|
+
|
|
306
|
+
const value = ref('')
|
|
307
|
+
</script>
|
|
308
|
+
|
|
309
|
+
<template>
|
|
310
|
+
<VJoyLabel for="custom-label">Payment method</VJoyLabel>
|
|
311
|
+
|
|
312
|
+
<VJoySelect id="custom-label" v-model="value" name="custom-label">
|
|
313
|
+
<option disabled value="">Select a value</option>
|
|
314
|
+
<option value="card">Credit card</option>
|
|
315
|
+
<option value="transfer">Bank transfer</option>
|
|
316
|
+
</VJoySelect>
|
|
317
|
+
</template>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
In Nuxt (or apps with auto-import), omit the import if `VJoySelect` is already global.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Related
|
|
325
|
+
|
|
326
|
+
| Component | Use when |
|
|
327
|
+
| --- | --- |
|
|
328
|
+
| `VJoyLabel` | External labels, legends, or richer label content than the `label` string prop |
|
|
329
|
+
| `VJoyFormError` | Validation messages below the select |
|
|
330
|
+
| `VJoyFormGroup` | Stacking label + select + error with consistent spacing |
|
|
331
|
+
| `VJoyRadioGroup` / `VJoyRadio` | Few mutually exclusive options that should stay visible |
|
|
332
|
+
| `VJoyCheckboxGroup` | Multiple selections |
|
|
333
|
+
| `VJoyDropdown` | Contextual menus from a custom trigger, not a labeled form field |
|
|
334
|
+
| `VJoySelectableItemGroup` | Card-style single or multi selection |
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## For agents
|
|
339
|
+
|
|
340
|
+
Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoySelect.dsds.json`](./JoySelect.dsds.json).
|
|
341
|
+
|
|
342
|
+
### Pick the right component
|
|
343
|
+
|
|
344
|
+
| If the intent is… | Emit… | Do not emit… |
|
|
345
|
+
| --- | --- | --- |
|
|
346
|
+
| Single choice from a predefined form list | `VJoySelect` + native `<option>` children | `VJoyDropdown` as a faux form select |
|
|
347
|
+
| 2–7 visible mutually exclusive options | `VJoyRadio` inside `VJoyRadioGroup` | `VJoySelect` hiding obvious choices |
|
|
348
|
+
| Multiple selections | `VJoyCheckboxGroup` | `VJoySelect` with `multiple` (not supported) |
|
|
349
|
+
| Overflow / action menu from a button | `VJoyDropdown` | `VJoySelect` |
|
|
350
|
+
| Rich card pickers | `VJoySelectableItemGroup` | Custom styled `<select>` |
|
|
351
|
+
| No visible label (toolbar filter) | `VJoySelect` with `ariaLabel` | Missing accessible name |
|
|
352
|
+
|
|
353
|
+
### Hard rules
|
|
354
|
+
|
|
355
|
+
1. **MUST** call `joy_get_component_info("VJoySelect")` and `joy_get_component_types("VJoySelect")` (or read types) before generating props.
|
|
356
|
+
2. **MUST** render options as native `<option>` elements in the default slot.
|
|
357
|
+
3. **MUST** set `name` (and `id` when using an external `VJoyLabel`) on every form field instance.
|
|
358
|
+
4. **MUST** bind value with `v-model` / `modelValue` + `@update:modelValue`.
|
|
359
|
+
5. **MUST NOT** invent props (`variant`, `placeholder`, `options` array prop, …).
|
|
360
|
+
6. **MUST NOT** override `.joy-select` border, padding, height, or radius in product CSS.
|
|
361
|
+
7. **MUST NOT** use the deprecated `select-label` slot in new code — use `label` or external `VJoyLabel`.
|
|
362
|
+
8. **SHOULD** include a disabled empty-value placeholder option when no default is selected.
|
|
363
|
+
9. **SHOULD** set `invalid` and render `VJoyFormError` when validation fails.
|
|
364
|
+
10. **SHOULD** use `size` from `TJoySelectSizes` only: `xsmall`, `small`, `medium`.
|
|
365
|
+
|
|
366
|
+
### Checklist
|
|
367
|
+
|
|
368
|
+
- [ ] Correct component (`VJoySelect` or named alternative)
|
|
369
|
+
- [ ] `size` from MCP enums only (`xsmall` | `small` | `medium`)
|
|
370
|
+
- [ ] Native `<option>` children in default slot (not invented `options` prop)
|
|
371
|
+
- [ ] `name` / `id` set; label associated (`label` prop, `VJoyLabel` `for`, or `ariaLabel`)
|
|
372
|
+
- [ ] No invented props / no custom select chrome CSS
|
|
373
|
+
- [ ] Placeholder option when nothing pre-selected
|
|
374
|
+
- [ ] `invalid` + visible error text when validation fails
|
|
375
|
+
- [ ] Parallel, concise option labels; stable `value` attributes
|
|
376
|
+
- [ ] Not using deprecated `select-label` slot
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## DSDS mapping
|
|
381
|
+
|
|
382
|
+
This Markdown mirrors [`JoySelect.dsds.json`](./JoySelect.dsds.json) (DSDS **0.15.2**).
|
|
383
|
+
|
|
384
|
+
| Section here | DSDS block |
|
|
385
|
+
| --- | --- |
|
|
386
|
+
| When to use / not | `use-cases` |
|
|
387
|
+
| Anatomy | `anatomy` |
|
|
388
|
+
| Variants (none) | — |
|
|
389
|
+
| Sizes | `variants` / `design-specifications` |
|
|
390
|
+
| States | `states` |
|
|
391
|
+
| Best practices / content rules | `guidelines` (+ `content`) |
|
|
392
|
+
| Accessibility | `accessibility` |
|
|
393
|
+
| Examples | `sections` |
|
|
394
|
+
| For agents | `agentDocumentBlocks` |
|
|
395
|
+
| Props detail | `api` in JSON + Joy MCP |
|
|
396
|
+
| Imports | `imports` in JSON |
|