@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,372 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: VJoyFloatingActionsBar
|
|
3
|
+
status: draft
|
|
4
|
+
language: en
|
|
5
|
+
dsds: JoyFloatingActionsBar.dsds.json
|
|
6
|
+
dsdsVersion: "0.15.2"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Floating actions bar
|
|
10
|
+
|
|
11
|
+
Use **`VJoyFloatingActionsBar`** when contextual next steps should stay visible at the bottom of the viewport — for example bulk actions after a multi-select, or a primary CTA while the user scrolls a long form.
|
|
12
|
+
|
|
13
|
+
It teleports to `body` by default, splits **`content`** (left) and **`actions`** (right) slots, and adapts from a floating card on desktop to a full-width bar on mobile.
|
|
14
|
+
|
|
15
|
+
| | |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| **Code** | `VJoyFloatingActionsBar` from `@maltjoy/core-vue` |
|
|
18
|
+
| **Design** | [ZeroHeight — Floating action bar](https://zeroheight.com/6bf479565/) (page id `8415609`) |
|
|
19
|
+
| **Storybook** | [VJoyFloatingActionsBar docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-overlays-vjoyfloatingactionsbar--docs) |
|
|
20
|
+
| **DSDS (machine)** | [`JoyFloatingActionsBar.dsds.json`](./JoyFloatingActionsBar.dsds.json) |
|
|
21
|
+
| **Live API** | `joy_get_component_info("VJoyFloatingActionsBar")` |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## When to use this component
|
|
26
|
+
|
|
27
|
+
Use a floating actions bar when a **user action changes what they can do next** — typically after selecting items in a list or table — and those next steps should remain reachable without scrolling back to a page footer.
|
|
28
|
+
|
|
29
|
+
The bar surfaces **flow-specific CTAs** (create mission, publish, submit) together with optional context on the left (selection count, avatars, short status). It appears in response to context; hide it when that context clears.
|
|
30
|
+
|
|
31
|
+
### When not to use this component
|
|
32
|
+
|
|
33
|
+
| Need | Use instead |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| Confirmation or status after an action completes | `pushVJoySnackbar` |
|
|
36
|
+
| Inline, persistent helper or field-level message | `VJoyHighlight` |
|
|
37
|
+
| Blocking decision or multi-field input | `VJoyDialog` |
|
|
38
|
+
| Slide-in panel with its own header/footer | `VJoyDrawer` |
|
|
39
|
+
| Actions that are always visible at the bottom of a form (not selection-driven) | Page layout / form footer — not a floating bar |
|
|
40
|
+
| Extra CTAs when the page already has bottom buttons for the same flow | Existing page footer — don’t duplicate actions |
|
|
41
|
+
| Custom fixed bottom bar with ad-hoc CSS | Still use `VJoyFloatingActionsBar` + Joy tokens |
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## How it works
|
|
46
|
+
|
|
47
|
+
### Anatomy
|
|
48
|
+
|
|
49
|
+
| Part | Required? | What it is |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| Teleport host | Yes | Renders into `appendTo` (default `body`) after mount |
|
|
52
|
+
| Bar container | Yes | White card (`joy-bgcolor-neutral-0`), elevation-2, radius-4 |
|
|
53
|
+
| Inner row | Yes | Flex row; horizontal alignment from `justify` |
|
|
54
|
+
| Content slot | No | Left area — selection summary, avatars, short copy |
|
|
55
|
+
| Actions slot | Yes (content) | Right area — one or more `VJoyButton` (or compatible controls) |
|
|
56
|
+
| Scoped modifier | No | `floating-actions-bar--scoped` when `appendTo` is not `body` |
|
|
57
|
+
|
|
58
|
+
### Default values
|
|
59
|
+
|
|
60
|
+
- `height` defaults to **`92px`**
|
|
61
|
+
- `appendTo` defaults to **`body`**
|
|
62
|
+
- `justify` defaults to **`space-between`**
|
|
63
|
+
|
|
64
|
+
Props and slots: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
|
|
65
|
+
|
|
66
|
+
### Layout and teleport
|
|
67
|
+
|
|
68
|
+
1. **Mount** — the bar teleports only after `onMounted` (avoids SSR / hydration issues).
|
|
69
|
+
2. **Desktop (default `appendTo`)** — `position: fixed`, ~80% width, centered, offset from bottom (`--joy-core-spacing-6`), respects `--layout-padding-left`.
|
|
70
|
+
3. **Mobile (≤ 991px)** — full width, flush to bottom, square corners.
|
|
71
|
+
4. **Scoped target** — when `appendTo` is not `body`, desktop uses `position: sticky`; mobile still uses `position: fixed`.
|
|
72
|
+
|
|
73
|
+
**Visibility** is controlled by the parent — wrap in `v-if` / `v-show` when selection or draft state changes. The component does not manage open/close itself.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Variants
|
|
78
|
+
|
|
79
|
+
`VJoyFloatingActionsBar` has no color variant prop. Choose **layout** via `justify` and **composition** via the two slots.
|
|
80
|
+
|
|
81
|
+
### `space-between` (default)
|
|
82
|
+
|
|
83
|
+
Content on the left, actions on the right — the common bulk-selection pattern (avatars + count + primary CTA).
|
|
84
|
+
|
|
85
|
+
Use when both slots carry meaning.
|
|
86
|
+
|
|
87
|
+
### `flex-end`
|
|
88
|
+
|
|
89
|
+
Actions aligned to the trailing edge; content slot omitted or empty.
|
|
90
|
+
|
|
91
|
+
Use for a single CTA or a right-aligned button group (publish workflows, submit bars).
|
|
92
|
+
|
|
93
|
+
### Other `justify` values
|
|
94
|
+
|
|
95
|
+
`center`, `space-around`, `space-evenly`, and `flex-start` are supported for edge layouts (Storybook, narrow canvases, centered toolbars). Prefer `space-between` or `flex-end` in product UI unless design specifies otherwise.
|
|
96
|
+
|
|
97
|
+
### Not in the API: `primaryActionLabel`
|
|
98
|
+
|
|
99
|
+
Tests or legacy snippets may reference `primaryActionLabel`. It is **not** on `VJoyFloatingActionsBar` props — wire actions only through the **`actions`** slot.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Sizes
|
|
104
|
+
|
|
105
|
+
Single default height — no `size` prop on the bar itself. Action density comes from **`VJoyButton` `size`** inside the `actions` slot (stories use `small`).
|
|
106
|
+
|
|
107
|
+
| Control | Use when |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| Default `height` (`92px`) | Standard button row + one line of content |
|
|
110
|
+
| Custom `height` | Taller content in the `content` slot (multi-line copy, stacked chips) — adjust only when default clips content |
|
|
111
|
+
|
|
112
|
+
**Don’t** hardcode bar width, shadow, or bottom offset in product CSS — the component owns layout tokens (`--joy-core-spacing-*`, `--joy-core-z-index-fixed-content`, `--layout-padding-left`).
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## States
|
|
117
|
+
|
|
118
|
+
| State | What the user sees / can do |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| Hidden (default) | Parent does not render the bar — no selection / no draft context |
|
|
121
|
+
| Visible (desktop) | Floating card above page content; actions reachable while scrolling |
|
|
122
|
+
| Visible (mobile) | Full-width bottom bar; may cover page footer — account for padding |
|
|
123
|
+
| Scoped (`appendTo` ≠ `body`) | Bar sticks within a canvas (drawer, Storybook, embedded preview) |
|
|
124
|
+
| Content only | `content` slot filled, `actions` empty — rare; usually pair with actions |
|
|
125
|
+
| Actions only | `justify="flex-end"`; trailing CTAs without left context |
|
|
126
|
+
| Multiple actions | Gap between buttons (`--joy-core-spacing-3`); follow button hierarchy (`main` + `secondary` / `ghost`) |
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Best practices
|
|
131
|
+
|
|
132
|
+
Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
|
|
133
|
+
|
|
134
|
+
### When to show
|
|
135
|
+
|
|
136
|
+
- **SHOULD** show the bar only when context warrants it (selection active, unsaved draft, publish step) — hide it when context clears.
|
|
137
|
+
- **SHOULD** reflect **next steps in the current flow** in the `actions` slot — not a grab bag of unrelated shortcuts.
|
|
138
|
+
- **MUST NOT** use the bar for action feedback (saved, error, sent) — use `pushVJoySnackbar`.
|
|
139
|
+
- **MUST NOT** duplicate CTAs already fixed at the bottom of the same page for the same decision.
|
|
140
|
+
|
|
141
|
+
### Hierarchy and composition
|
|
142
|
+
|
|
143
|
+
- **SHOULD** use at most one **`main`** button in `actions` per bar (see `VJoyButton` hierarchy).
|
|
144
|
+
- **SHOULD** order actions left-to-right by emphasis: `ghost` / `secondary` before `main` (Cancel · Save draft · Publish).
|
|
145
|
+
- **SHOULD** keep `content` slot to **short, scannable context** (count, avatars, one line of status) — not secondary page content.
|
|
146
|
+
- **SHOULD** use `justify="flex-end"` when there is no meaningful left content.
|
|
147
|
+
|
|
148
|
+
### Implementation
|
|
149
|
+
|
|
150
|
+
- **MUST** use only documented props and `justify` enum values from MCP / `VJoyFloatingActionsBar.vue`.
|
|
151
|
+
- **MUST** place CTAs in the **`actions`** slot — no invented `primaryActionLabel` or similar props.
|
|
152
|
+
- **MUST** use Joy tokens — no custom `.floating-actions-bar` chrome (background, shadow, radius, position).
|
|
153
|
+
- **MUST NOT** invent props (`variant`, `open`, `primaryAction`, `visible`, …).
|
|
154
|
+
- **SHOULD** use `VJoyButton` with `size="small"` in the bar for consistent density.
|
|
155
|
+
- **SHOULD** set `appendTo` to a scoped selector in Storybook, drawers, or tests — default `body` for app pages.
|
|
156
|
+
|
|
157
|
+
### Do / don’t
|
|
158
|
+
|
|
159
|
+
| Do | Don’t |
|
|
160
|
+
| --- | --- |
|
|
161
|
+
| `v-if` when selection count > 0 | Always-visible bar with no context |
|
|
162
|
+
| `content`: “3 freelancers selected” + avatars | Paragraphs of help text in `content` |
|
|
163
|
+
| `actions`: flow CTAs (`Create mission`, `Publish`) | Snackbar-style “Saved successfully” |
|
|
164
|
+
| One `main` + companions in `actions` | Two competing `main` buttons |
|
|
165
|
+
| Hide bar when context clears | Leave bar up after deselect all |
|
|
166
|
+
| `justify="flex-end"` for actions-only | Empty `content` with default `space-between` |
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Content
|
|
171
|
+
|
|
172
|
+
The bar carries **context on the left** and **action labels on the right**. Keep both scannable at a glance.
|
|
173
|
+
|
|
174
|
+
### Content slot formula
|
|
175
|
+
|
|
176
|
+
**Count + object** or **short status** tied to the selection:
|
|
177
|
+
|
|
178
|
+
- ✅ 3 freelancers selected
|
|
179
|
+
- ✅ 12 files selected
|
|
180
|
+
- ✅ Unsaved changes
|
|
181
|
+
|
|
182
|
+
Use `VJoyText`, `VJoyAvatarsList`, and `VJoyWrapper` as in Storybook — don’t invent new summary patterns.
|
|
183
|
+
|
|
184
|
+
### Action label formula
|
|
185
|
+
|
|
186
|
+
Follow **`VJoyButton`** rules — **verb + noun**, ≤ 25 characters:
|
|
187
|
+
|
|
188
|
+
- ✅ Create mission
|
|
189
|
+
- ✅ Publish
|
|
190
|
+
- ✅ Save draft
|
|
191
|
+
- ✅ Submit
|
|
192
|
+
|
|
193
|
+
Destructive or irreversible actions name the object: ✅ `Delete projects (3)`.
|
|
194
|
+
|
|
195
|
+
### Writing rules
|
|
196
|
+
|
|
197
|
+
| Strength | Rule |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| **MUST** | Content slot states **what is selected or pending** — not marketing copy |
|
|
200
|
+
| **MUST** | Action labels describe the **next step**, not generic “Submit” / “Click” |
|
|
201
|
+
| **MUST NOT** | Use the bar for success/error toasts |
|
|
202
|
+
| **MUST NOT** | Push secondary page content into `content` to fill space |
|
|
203
|
+
| **SHOULD** | Include a numeric count when multiple items are selected |
|
|
204
|
+
| **SHOULD** | Match terminology with the list/table above (same noun: freelancer, file, mission) |
|
|
205
|
+
| **SHOULD** | Use sentence case for content; button labels follow button guidelines |
|
|
206
|
+
| **SHOULD** (FR) | Follow French grammar for counts and labels when the UI is localized |
|
|
207
|
+
|
|
208
|
+
### Patterns
|
|
209
|
+
|
|
210
|
+
**Bulk selection** — `content`: compressed avatars + count. `actions`: single `main` CTA for the bulk operation.
|
|
211
|
+
|
|
212
|
+
**Publish / save workflow** — `justify="flex-end"`. `actions`: `ghost` Cancel · `secondary` Save draft · `main` Publish.
|
|
213
|
+
|
|
214
|
+
**Long form submit** — `actions` only, `justify="flex-end"`, one `main` Submit while the user scrolls — only if there is no duplicate footer CTA.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Accessibility
|
|
219
|
+
|
|
220
|
+
Behaviors (what must happen), not slogans.
|
|
221
|
+
|
|
222
|
+
| Behavior | Expectation |
|
|
223
|
+
| --- | --- |
|
|
224
|
+
| Role | Layout container — not a dialog; actions inside use native buttons from `VJoyButton` |
|
|
225
|
+
| Name | Selection context in `content` must be readable text (not color-only); buttons keep their own labels |
|
|
226
|
+
| Keyboard | All `actions` controls must be focusable and operable via keyboard — use `VJoyButton`, not clickable `<div>`s |
|
|
227
|
+
| Focus | Bar does not trap focus; tab order follows DOM order (content then actions) |
|
|
228
|
+
| Visibility | Parent `v-if` should remove the bar from the tab order when context clears |
|
|
229
|
+
| Mobile | Full-width bar may cover content — ensure page has bottom padding or the bar hides when not needed |
|
|
230
|
+
| Motion | No enter/leave animation in the component — avoid jarring show/hide without app-level consideration |
|
|
231
|
+
| Z-index | Uses `--joy-core-z-index-fixed-content` — don’t stack opaque overlays that hide the bar’s actions without reason |
|
|
232
|
+
|
|
233
|
+
Target: **WCAG 2.2 AA** where the product requires it.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Examples
|
|
238
|
+
|
|
239
|
+
```vue
|
|
240
|
+
<script setup lang="ts">
|
|
241
|
+
import { computed, ref } from 'vue'
|
|
242
|
+
import {
|
|
243
|
+
VJoyFloatingActionsBar,
|
|
244
|
+
VJoyButton,
|
|
245
|
+
VJoyText,
|
|
246
|
+
VJoyWrapper,
|
|
247
|
+
VJoyAvatarsList,
|
|
248
|
+
VJoyAvatar,
|
|
249
|
+
} from '@maltjoy/core-vue'
|
|
250
|
+
|
|
251
|
+
const selectedIds = ref<string[]>(['a', 'b', 'c'])
|
|
252
|
+
const hasSelection = computed(() => selectedIds.value.length > 0)
|
|
253
|
+
</script>
|
|
254
|
+
|
|
255
|
+
<template>
|
|
256
|
+
<!-- Bulk selection -->
|
|
257
|
+
<VJoyFloatingActionsBar v-if="hasSelection">
|
|
258
|
+
<template #content>
|
|
259
|
+
<VJoyWrapper no-margin column-gap="3" align="center">
|
|
260
|
+
<VJoyAvatarsList compress>
|
|
261
|
+
<VJoyAvatar full-name="Alice Dupont" size="small" />
|
|
262
|
+
<VJoyAvatar full-name="Bob Martin" size="small" />
|
|
263
|
+
<VJoyAvatar full-name="Carol White" size="small" />
|
|
264
|
+
</VJoyAvatarsList>
|
|
265
|
+
<VJoyText preset="primary-400">
|
|
266
|
+
{{ selectedIds.length }} freelancers selected
|
|
267
|
+
</VJoyText>
|
|
268
|
+
</VJoyWrapper>
|
|
269
|
+
</template>
|
|
270
|
+
<template #actions>
|
|
271
|
+
<VJoyButton variant="main" size="small" label="Create mission" />
|
|
272
|
+
</template>
|
|
273
|
+
</VJoyFloatingActionsBar>
|
|
274
|
+
|
|
275
|
+
<!-- Actions only — publish workflow -->
|
|
276
|
+
<VJoyFloatingActionsBar justify="flex-end">
|
|
277
|
+
<template #actions>
|
|
278
|
+
<VJoyButton variant="ghost" size="small" label="Cancel" />
|
|
279
|
+
<VJoyButton variant="secondary" size="small" label="Save draft" />
|
|
280
|
+
<VJoyButton variant="main" size="small" label="Publish" />
|
|
281
|
+
</template>
|
|
282
|
+
</VJoyFloatingActionsBar>
|
|
283
|
+
</template>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Storybook / scoped canvas — teleport into a host element:
|
|
287
|
+
|
|
288
|
+
```vue
|
|
289
|
+
<VJoyFloatingActionsBar append-to="#my-canvas">
|
|
290
|
+
<template #actions>
|
|
291
|
+
<VJoyButton variant="main" size="small" label="Submit" />
|
|
292
|
+
</template>
|
|
293
|
+
</VJoyFloatingActionsBar>
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
In Nuxt (or apps with auto-import), omit the import if `VJoyFloatingActionsBar` is already global.
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Related
|
|
301
|
+
|
|
302
|
+
| Component | Use when |
|
|
303
|
+
| --- | --- |
|
|
304
|
+
| `VJoyButton` | CTAs in the `actions` slot |
|
|
305
|
+
| `VJoyText` | Short status copy in `content` |
|
|
306
|
+
| `VJoyAvatarsList` / `VJoyAvatar` | Selected people in `content` |
|
|
307
|
+
| `VJoyWrapper` | Horizontal layout inside `content` |
|
|
308
|
+
| `pushVJoySnackbar` | Transient feedback after an action |
|
|
309
|
+
| `VJoyHighlight` | Inline persistent messages |
|
|
310
|
+
| `VJoyDialog` | Blocking confirm or form |
|
|
311
|
+
| `VJoyDrawer` | Side panel with its own action area |
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## For agents
|
|
316
|
+
|
|
317
|
+
Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoyFloatingActionsBar.dsds.json`](./JoyFloatingActionsBar.dsds.json).
|
|
318
|
+
|
|
319
|
+
### Pick the right component
|
|
320
|
+
|
|
321
|
+
| If the intent is… | Emit… | Do not emit… |
|
|
322
|
+
| --- | --- | --- |
|
|
323
|
+
| Bulk / contextual bottom actions while scrolling | `VJoyFloatingActionsBar` + slots | Custom `position: fixed` div |
|
|
324
|
+
| Toast after save/send | `pushVJoySnackbar` | Floating bar with success text |
|
|
325
|
+
| Inline error under a field | `VJoyHighlight` | Bar in `content` slot |
|
|
326
|
+
| Modal confirm | `VJoyDialog` | Bar with only Cancel/Confirm |
|
|
327
|
+
| Always-on form footer (no selection) | Page footer layout | `VJoyFloatingActionsBar` always mounted |
|
|
328
|
+
| Primary CTA prop | `#actions` slot + `VJoyButton` | `primaryActionLabel` (invalid) |
|
|
329
|
+
|
|
330
|
+
### Hard rules
|
|
331
|
+
|
|
332
|
+
1. **MUST** call `joy_get_component_info("VJoyFloatingActionsBar")` (or read `VJoyFloatingActionsBar.vue`) before generating props.
|
|
333
|
+
2. **MUST** put buttons in the **`actions`** slot — no invented action props.
|
|
334
|
+
3. **MUST** control visibility with parent `v-if` / `v-show` when context applies.
|
|
335
|
+
4. **MUST** use only `justify` values from the documented enum.
|
|
336
|
+
5. **MUST NOT** invent props (`primaryActionLabel`, `open`, `variant`, `visible`, …).
|
|
337
|
+
6. **MUST NOT** override `.floating-actions-bar` position, width, shadow, or background in app CSS.
|
|
338
|
+
7. **SHOULD** use `justify="flex-end"` when `content` is empty.
|
|
339
|
+
8. **SHOULD** use `VJoyButton` `size="small"` in `actions`.
|
|
340
|
+
9. **SHOULD** use at most one `variant="main"` button per bar.
|
|
341
|
+
|
|
342
|
+
### Checklist
|
|
343
|
+
|
|
344
|
+
- [ ] Correct component (or named alternative from table above)
|
|
345
|
+
- [ ] Props / `justify` enum from MCP only
|
|
346
|
+
- [ ] Actions in `#actions` slot — no `primaryActionLabel`
|
|
347
|
+
- [ ] Parent controls show/hide when context changes
|
|
348
|
+
- [ ] No invented props / no custom bar chrome CSS
|
|
349
|
+
- [ ] Not used for snackbar-style feedback
|
|
350
|
+
- [ ] No duplicate footer CTAs for the same flow
|
|
351
|
+
- [ ] Button labels follow `VJoyButton` content rules
|
|
352
|
+
- [ ] `content` is short context — not secondary page content
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## DSDS mapping
|
|
357
|
+
|
|
358
|
+
This Markdown mirrors [`JoyFloatingActionsBar.dsds.json`](./JoyFloatingActionsBar.dsds.json) (DSDS **0.15.2**).
|
|
359
|
+
|
|
360
|
+
| Section here | DSDS block |
|
|
361
|
+
| --- | --- |
|
|
362
|
+
| When to use / not | `use-cases` |
|
|
363
|
+
| Anatomy / layout | `anatomy` |
|
|
364
|
+
| `justify` / composition | `variants` |
|
|
365
|
+
| Height / responsive layout | `design-specifications` |
|
|
366
|
+
| States | `states` |
|
|
367
|
+
| Best practices / content rules | `guidelines` (+ `content`) |
|
|
368
|
+
| Accessibility | `accessibility` |
|
|
369
|
+
| Examples | `sections` |
|
|
370
|
+
| For agents | `agentDocumentBlocks` |
|
|
371
|
+
| Props detail | `api` in JSON + Joy MCP |
|
|
372
|
+
| Imports | `imports` in JSON |
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: VJoyFormError
|
|
3
|
+
status: draft
|
|
4
|
+
language: en
|
|
5
|
+
dsds: JoyFormError.dsds.json
|
|
6
|
+
dsdsVersion: "0.15.2"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Form error
|
|
10
|
+
|
|
11
|
+
Use **`VJoyFormError`** when a form field or group failed validation and the user needs a **persistent, inline** explanation directly below the control.
|
|
12
|
+
|
|
13
|
+
It shows a warning icon, error-colored text (`--joy-color-error-50`), and `role="alert"` so assistive tech announces the message. Pass a string array via `errors`, or use the default slot when you need custom markup (for example a hand-built `<ul>` / `<li>` list).
|
|
14
|
+
|
|
15
|
+
| | |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| **Code** | `VJoyFormError` from `@maltjoy/core-vue` |
|
|
18
|
+
| **Design** | [Figma — Joy Core Design System](https://www.figma.com/design/jfndp2QBJGS9yeuWbeGuvD/) |
|
|
19
|
+
| **Storybook** | [VJoyFormError docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-forms-vjoyformerror--docs) |
|
|
20
|
+
| **DSDS (machine)** | [`JoyFormError.dsds.json`](./JoyFormError.dsds.json) |
|
|
21
|
+
| **Live API** | `joy_get_component_info("VJoyFormError")` |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## When to use this component
|
|
26
|
+
|
|
27
|
+
Use a form error when validation failed for a specific field or group and the user must read **what went wrong** and **how to fix it** before continuing.
|
|
28
|
+
|
|
29
|
+
Place it **immediately below** the related control (input, select, checkbox group, dropzone, etc.). Pair it with the field’s `invalid` state so error styling is consistent on both the control and the message.
|
|
30
|
+
|
|
31
|
+
For a single message, pass one string in `errors`. For several related failures on the same field, pass multiple strings or render a `<ul>` / `<li>` list in the default slot.
|
|
32
|
+
|
|
33
|
+
### When not to use this component
|
|
34
|
+
|
|
35
|
+
| Need | Use instead |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| Page-level or section-level failure (not tied to one field) | `VJoyHighlight` (`level="error"`) |
|
|
38
|
+
| Transient feedback after an action (saved, failed request) | `pushVJoySnackbar` |
|
|
39
|
+
| Blocking decision or multi-step correction | `VJoyDialog` |
|
|
40
|
+
| Helper text before any error occurs | `VJoyLabel` legend slot, field description, or `VJoyHighlight` (`level="info"`) |
|
|
41
|
+
| Custom red text / icon row with ad-hoc CSS | Still use `VJoyFormError` + Joy tokens — don’t reinvent error chrome |
|
|
42
|
+
|
|
43
|
+
> **Deprecated:** `visible` — inject or remove the component with `v-if` / `v-show` instead. The prop remains in the API for legacy markup but should not be used on new work.
|
|
44
|
+
|
|
45
|
+
> **Not in the API:** `message` — pass copy via the `errors` prop or the default slot. Do not emit `message="…"`.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## How it works
|
|
50
|
+
|
|
51
|
+
### Anatomy
|
|
52
|
+
|
|
53
|
+
| Part | Required? | What it is |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| Root container | Yes | `div` with `data-joy-form-error`, `role="alert"`, class `joy-form-error` |
|
|
56
|
+
| Warning icon | Yes | `VJoyIcon` (`warning-triangle`), class `joy-icon-error` |
|
|
57
|
+
| Message body | Yes | Default slot **or** auto-generated `<ul>` / `<li>` from `errors` |
|
|
58
|
+
| Bottom spacing | Yes* | Default `margin-bottom: var(--joy-core-spacing-5)` — *removed when `noMargin` is true |
|
|
59
|
+
|
|
60
|
+
The root is **not rendered** when there is no default slot content and `errors` is empty or omitted.
|
|
61
|
+
|
|
62
|
+
### Default values
|
|
63
|
+
|
|
64
|
+
- `noMargin` defaults to **`false`**
|
|
65
|
+
- `visible` defaults to **`true`** (deprecated — prefer conditional rendering)
|
|
66
|
+
- `errors` is **optional** — omit when using the default slot
|
|
67
|
+
|
|
68
|
+
Props and slots: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
|
|
69
|
+
|
|
70
|
+
### Content modes
|
|
71
|
+
|
|
72
|
+
| Mode | When to use |
|
|
73
|
+
| --- | --- |
|
|
74
|
+
| `errors` prop | One or more plain strings; component renders `<ul>` / `<li>` automatically |
|
|
75
|
+
| Default slot | Custom HTML (multiple lines, mixed content, framework-templated markup) |
|
|
76
|
+
|
|
77
|
+
When both slot and `errors` are provided, the **slot takes precedence** (`errors` is ignored).
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## States
|
|
82
|
+
|
|
83
|
+
| State | What the user sees / can do |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| Hidden (no content) | Nothing in the DOM — no `errors`, no default slot |
|
|
86
|
+
| Visible (single error) | Icon + one line of error text |
|
|
87
|
+
| Visible (multiple errors) | Icon + bulletless list (`<ul>` / `<li>`) |
|
|
88
|
+
| `noMargin` | Same visible error without bottom margin (parent handles spacing, e.g. `VJoyFormGroup`) |
|
|
89
|
+
|
|
90
|
+
There is no hover, focus, or disabled state — the component is presentational feedback, not interactive.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Best practices
|
|
95
|
+
|
|
96
|
+
Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
|
|
97
|
+
|
|
98
|
+
### Placement and pairing
|
|
99
|
+
|
|
100
|
+
- **MUST** place `VJoyFormError` directly below the field or group it describes.
|
|
101
|
+
- **MUST** set `invalid` on the related form control when showing an error — red border or outline alone is not enough.
|
|
102
|
+
- **MUST** show the error only when validation has failed (typically `v-if` on a boolean or error string).
|
|
103
|
+
- **SHOULD** show one focused message per field when possible; use multiple list items only when several distinct fixes are required.
|
|
104
|
+
- **MUST NOT** use `VJoyFormError` for success or neutral helper copy.
|
|
105
|
+
|
|
106
|
+
### Implementation
|
|
107
|
+
|
|
108
|
+
- **MUST** use only documented props (`errors`, `noMargin`) and the default slot from MCP or `VJoyFormError.vue`.
|
|
109
|
+
- **MUST** style with Joy tokens — no custom `.joy-form-error` chrome (color, icon, spacing).
|
|
110
|
+
- **MUST NOT** invent props (`message`, `variant`, `size`, `level`, …).
|
|
111
|
+
- **MUST NOT** rely on the deprecated `visible` prop — use `v-if` / `v-show`.
|
|
112
|
+
- **SHOULD** use `noMargin` when the parent layout (`VJoyFormGroup`, `VJoyWrapper`) already applies field spacing.
|
|
113
|
+
- **SHOULD** prefer `errors` for simple string messages; use the slot only when markup cannot be expressed as strings.
|
|
114
|
+
|
|
115
|
+
### Do / don’t
|
|
116
|
+
|
|
117
|
+
| Do | Don’t |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| `invalid` on field + `VJoyFormError` below | Red field border with no message |
|
|
120
|
+
| `:errors="['Enter a valid email address']"` | `message="…"` prop |
|
|
121
|
+
| `v-if="showError"` to toggle visibility | Deprecated `visible={false}` in DOM |
|
|
122
|
+
| Short, actionable copy | Generic `Invalid` or `Error` |
|
|
123
|
+
| `noMargin` inside `VJoyFormGroup` when spacing doubles | Stacking default margin on top of group gap |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Content
|
|
128
|
+
|
|
129
|
+
Error copy must tell the user what failed and what to do next.
|
|
130
|
+
|
|
131
|
+
### Message formula
|
|
132
|
+
|
|
133
|
+
**What went wrong + how to fix it** (imperative or instructive):
|
|
134
|
+
|
|
135
|
+
- ✅ `Enter a valid email address`
|
|
136
|
+
- ✅ `Password must be at least 8 characters`
|
|
137
|
+
- ✅ `Choose a delivery method`
|
|
138
|
+
|
|
139
|
+
Avoid blame, jargon, or internal codes unless the user needs them.
|
|
140
|
+
|
|
141
|
+
### Single vs multiple messages
|
|
142
|
+
|
|
143
|
+
| Pattern | When |
|
|
144
|
+
| --- | --- |
|
|
145
|
+
| One string in `errors` | Single rule failed (format, required, length) |
|
|
146
|
+
| Multiple `errors` entries or `<li>` items | Several independent fixes on the same field (rare — prefer one clear message) |
|
|
147
|
+
|
|
148
|
+
Keep list items parallel (same grammatical structure).
|
|
149
|
+
|
|
150
|
+
### Writing rules
|
|
151
|
+
|
|
152
|
+
| Strength | Rule |
|
|
153
|
+
| --- | --- |
|
|
154
|
+
| **MUST** | State what is wrong and how to fix it — ✅ `Enter a valid email address` · ❌ `Invalid` |
|
|
155
|
+
| **MUST** | Use sentence case; no trailing period for single-line errors unless product copy requires it |
|
|
156
|
+
| **MUST NOT** | Repeat the field label as the entire message — ❌ `Email` |
|
|
157
|
+
| **MUST NOT** | Expose raw API / stack traces to end users |
|
|
158
|
+
| **SHOULD** | Keep messages ≤ ~80 characters when possible |
|
|
159
|
+
| **SHOULD** | Match terminology with the field label (`Email address` label → `Enter a valid email address`) |
|
|
160
|
+
| **SHOULD** | Localize all strings passed to `errors` or the slot |
|
|
161
|
+
| **SHOULD** (FR) | Use correct gender/agreement for past-participle adjectives in French error strings |
|
|
162
|
+
|
|
163
|
+
### Patterns
|
|
164
|
+
|
|
165
|
+
**Required field on submit** — Show after submit or blur: `You must enter a name`.
|
|
166
|
+
|
|
167
|
+
**Format validation** — After blur or when format is unambiguous: `Enter a valid email address`.
|
|
168
|
+
|
|
169
|
+
**Group validation** — One `VJoyFormError` below `VJoyRadioGroup` / `VJoyCheckboxGroup`: `Select one option to continue`.
|
|
170
|
+
|
|
171
|
+
**Composed fields** — `VJoyTagsInput` and similar components use the default slot for multiple error types in one list.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Accessibility
|
|
176
|
+
|
|
177
|
+
Behaviors (what must happen), not slogans.
|
|
178
|
+
|
|
179
|
+
| Behavior | Expectation |
|
|
180
|
+
| --- | --- |
|
|
181
|
+
| Role | `role="alert"` on the root — announces when the error appears |
|
|
182
|
+
| Visibility | Render with `v-if` when there is an error; component omits itself when `errors` is empty and slot is empty |
|
|
183
|
+
| Name | Message text is plain content inside the alert — no extra `aria-label` needed when copy is descriptive |
|
|
184
|
+
| Icon | Warning triangle is decorative; meaning is carried by the text |
|
|
185
|
+
| Field association | Pair with `invalid` on the control; associate label + field with `VJoyLabel` / `for` + `id` |
|
|
186
|
+
| Timing | Show errors after submit, blur, or when the value is clearly invalid — avoid shouting on first keystroke unless format is unambiguous |
|
|
187
|
+
| Color | Error color (`--joy-color-error-50`) supplements text — never the only signal |
|
|
188
|
+
|
|
189
|
+
Target: **WCAG 2.2 AA** where the product requires it.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Examples
|
|
194
|
+
|
|
195
|
+
```vue
|
|
196
|
+
<script setup lang="ts">
|
|
197
|
+
import { ref } from 'vue'
|
|
198
|
+
import {
|
|
199
|
+
VJoyInput,
|
|
200
|
+
VJoyFormError,
|
|
201
|
+
VJoyFormGroup,
|
|
202
|
+
VJoyLabel,
|
|
203
|
+
VJoyCheckbox,
|
|
204
|
+
} from '@maltjoy/core-vue'
|
|
205
|
+
|
|
206
|
+
const email = ref('')
|
|
207
|
+
const showEmailError = ref(false)
|
|
208
|
+
const accepted = ref(false)
|
|
209
|
+
|
|
210
|
+
function validateEmail() {
|
|
211
|
+
showEmailError.value = !email.value.includes('@')
|
|
212
|
+
}
|
|
213
|
+
</script>
|
|
214
|
+
|
|
215
|
+
<template>
|
|
216
|
+
<!-- Single error via errors prop -->
|
|
217
|
+
<VJoyFormGroup>
|
|
218
|
+
<VJoyInput
|
|
219
|
+
v-model="email"
|
|
220
|
+
name="email"
|
|
221
|
+
label="Email address"
|
|
222
|
+
:invalid="showEmailError"
|
|
223
|
+
@blur="validateEmail"
|
|
224
|
+
/>
|
|
225
|
+
<VJoyFormError
|
|
226
|
+
v-if="showEmailError"
|
|
227
|
+
:errors="['Enter a valid email address']"
|
|
228
|
+
/>
|
|
229
|
+
</VJoyFormGroup>
|
|
230
|
+
|
|
231
|
+
<!-- Multiple errors via slot -->
|
|
232
|
+
<VJoyFormError v-if="showEmailError">
|
|
233
|
+
<ul>
|
|
234
|
+
<li>Email is not valid</li>
|
|
235
|
+
<li>Password is not strong enough</li>
|
|
236
|
+
</ul>
|
|
237
|
+
</VJoyFormError>
|
|
238
|
+
|
|
239
|
+
<!-- Checkbox with noMargin inside a group -->
|
|
240
|
+
<VJoyFormGroup>
|
|
241
|
+
<VJoyLabel for="terms">Terms and conditions</VJoyLabel>
|
|
242
|
+
<VJoyCheckbox id="terms" v-model="accepted" value="yes">
|
|
243
|
+
I accept the terms
|
|
244
|
+
</VJoyCheckbox>
|
|
245
|
+
<VJoyFormError
|
|
246
|
+
v-if="!accepted"
|
|
247
|
+
no-margin
|
|
248
|
+
:errors="['You must accept the terms and conditions']"
|
|
249
|
+
/>
|
|
250
|
+
</VJoyFormGroup>
|
|
251
|
+
</template>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
In Nuxt (or apps with auto-import), omit the import if `VJoyFormError` is already global.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Related
|
|
259
|
+
|
|
260
|
+
| Component | Use when |
|
|
261
|
+
| --- | --- |
|
|
262
|
+
| `VJoyInput` / `VJoyTextarea` / `VJoySelect` | Fields that accept `invalid` and sit above the error |
|
|
263
|
+
| `VJoyLabel` | Accessible label and optional legend above the field |
|
|
264
|
+
| `VJoyFormGroup` | Groups label, control, and error with consistent spacing |
|
|
265
|
+
| `VJoyWrapper` | Column layout between fields when not using `VJoyFormGroup` |
|
|
266
|
+
| `VJoyRadioGroup` / `VJoyCheckboxGroup` | Group-level validation with one error below the set |
|
|
267
|
+
| `VJoyHighlight` | Section- or page-level error/info banners |
|
|
268
|
+
| `pushVJoySnackbar` | Transient action feedback, not inline validation |
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## For agents
|
|
273
|
+
|
|
274
|
+
Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoyFormError.dsds.json`](./JoyFormError.dsds.json).
|
|
275
|
+
|
|
276
|
+
### Pick the right component
|
|
277
|
+
|
|
278
|
+
| If the intent is… | Emit… | Do not emit… |
|
|
279
|
+
| --- | --- | --- |
|
|
280
|
+
| Inline validation under a field | `VJoyFormError` + `invalid` on the field | Raw `<p class="error">` |
|
|
281
|
+
| Single error string | `VJoyFormError` with `:errors="['…']"` | `message="…"` prop |
|
|
282
|
+
| Multiple errors, custom markup | `VJoyFormError` + default slot (`<ul>` / `<li>`) | Multiple separate alert divs |
|
|
283
|
+
| Page / banner error | `VJoyHighlight` `level="error"` | `VJoyFormError` at page top |
|
|
284
|
+
| Toast after save/fail | `pushVJoySnackbar` | `VJoyFormError` |
|
|
285
|
+
|
|
286
|
+
### Hard rules
|
|
287
|
+
|
|
288
|
+
1. **MUST** call `joy_get_component_info("VJoyFormError")` (or read `VJoyFormError.vue`) before generating props.
|
|
289
|
+
2. **MUST** pair with `invalid` on the related form control when validation failed.
|
|
290
|
+
3. **MUST** pass messages via `errors` (string array) or the default slot — **not** a `message` prop.
|
|
291
|
+
4. **MUST** use `v-if` (or parent conditional) to show/hide errors — **not** deprecated `visible`.
|
|
292
|
+
5. **MUST NOT** invent props (`message`, `variant`, `size`, `level`, …) or custom `.joy-form-error` CSS.
|
|
293
|
+
6. **MUST NOT** render `VJoyFormError` when there is no error content (empty `errors` renders nothing).
|
|
294
|
+
7. **SHOULD** use `noMargin` when nested in `VJoyFormGroup` or when parent gap already applies.
|
|
295
|
+
8. **SHOULD** keep copy actionable: what failed + how to fix.
|
|
296
|
+
|
|
297
|
+
### Checklist
|
|
298
|
+
|
|
299
|
+
- [ ] Correct component (inline field error, not snackbar/highlight)
|
|
300
|
+
- [ ] Props from MCP / source only (`errors`, `noMargin`, default slot)
|
|
301
|
+
- [ ] Related field has `invalid` when error is shown
|
|
302
|
+
- [ ] `v-if` (or equivalent) — no `visible` prop
|
|
303
|
+
- [ ] No `message` prop / no invented props
|
|
304
|
+
- [ ] No custom error chrome CSS
|
|
305
|
+
- [ ] Actionable error copy (not bare `Invalid`)
|
|
306
|
+
- [ ] Placed directly below the described control
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## DSDS mapping
|
|
311
|
+
|
|
312
|
+
This Markdown mirrors [`JoyFormError.dsds.json`](./JoyFormError.dsds.json) (DSDS **0.15.2**).
|
|
313
|
+
|
|
314
|
+
| Section here | DSDS block |
|
|
315
|
+
| --- | --- |
|
|
316
|
+
| When to use / not | `use-cases` |
|
|
317
|
+
| Anatomy | `anatomy` |
|
|
318
|
+
| States | `states` |
|
|
319
|
+
| Best practices / content rules | `guidelines` (+ `content`) |
|
|
320
|
+
| Accessibility | `accessibility` |
|
|
321
|
+
| Examples | `sections` |
|
|
322
|
+
| For agents | `agentDocumentBlocks` |
|
|
323
|
+
| Props detail | `api` in JSON + Joy MCP |
|
|
324
|
+
| Imports | `imports` in JSON |
|
|
325
|
+
| Error color / typography | `design-specifications` (`--form-error-color`, `--joy-color-error-50`, `primary-300`) |
|