@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,414 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: VJoyInputNumber
|
|
3
|
+
status: draft
|
|
4
|
+
language: en
|
|
5
|
+
dsds: JoyInputNumber.dsds.json
|
|
6
|
+
dsdsVersion: "0.15.2"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Input Number
|
|
10
|
+
|
|
11
|
+
Use **`VJoyInputNumber`** when the user must enter a numeric value that is shown with a **unit suffix** on the right — amounts (€), percentages (%), durations (days), or other quantities where the unit is fixed for the field.
|
|
12
|
+
|
|
13
|
+
It is built on `VJoyInput` with `type="number"` by default, supports labels and validation states, and binds to `v-model`. Native attributes such as `placeholder`, `min`, `max`, `step`, and `aria-*` pass through to the underlying `<input>`.
|
|
14
|
+
|
|
15
|
+
| | |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| **Code** | `VJoyInputNumber` from `@maltjoy/core-vue` |
|
|
18
|
+
| **Design** | [Figma — Joy Core Design System](https://www.figma.com/design/jfndp2QBJGS9yeuWbeGuvD/) |
|
|
19
|
+
| **Storybook** | [VJoyInputNumber docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-forms-vjoyinputnumber--docs) |
|
|
20
|
+
| **DSDS (machine)** | [`JoyInputNumber.dsds.json`](./JoyInputNumber.dsds.json) |
|
|
21
|
+
| **Live API** | `joy_get_component_info("VJoyInputNumber")` |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## When to use this component
|
|
26
|
+
|
|
27
|
+
Use a number input with unit when someone must type or adjust a quantity and the unit is **known and fixed** for that field — for example a price in euros, a percentage, or a duration in days.
|
|
28
|
+
|
|
29
|
+
Pair it with a visible label, validation feedback when needed, and (inside forms) `VJoyWrapper` for consistent spacing. Set `unit` to the symbol or short suffix users expect; use `unit=""` when no suffix should appear but you still want number-input behaviour from this component.
|
|
30
|
+
|
|
31
|
+
### When not to use this component
|
|
32
|
+
|
|
33
|
+
| Need | Use instead |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| Free-form text (names, emails, references) | `VJoyInput` |
|
|
36
|
+
| Numeric value **without** a unit suffix | `VJoyInput` with `type="number"` |
|
|
37
|
+
| Password with show/hide toggle | `VJoyInputPassword` |
|
|
38
|
+
| Multi-line text | `VJoyTextarea` |
|
|
39
|
+
| One-time / verification code (OTP) | `VJoyInputDigit` |
|
|
40
|
+
| Choosing from a known set of options | `VJoySelect`, `VJoyRadio`, or `VJoySelectableItemGroup` |
|
|
41
|
+
| Numeric value with a **clear** action | `VJoyInput` with `type="number"` and `clearable` — `VJoyInputNumber` does not support `clearable` |
|
|
42
|
+
| `unit` on `VJoyInput` (legacy) | `VJoyInputNumber` — the `unit` prop was removed from `VJoyInput` in v6 |
|
|
43
|
+
| Custom-styled `<input>` / ad-hoc field chrome | Still use `VJoyInputNumber` + Joy tokens |
|
|
44
|
+
|
|
45
|
+
> **Deprecated:** do not use the default slot for labels on new work — prefer the `label` prop or a separate `VJoyLabel`. Do not pass `clearable` or `clearAriaLabel` — they are stripped and not supported on this component.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## How it works
|
|
50
|
+
|
|
51
|
+
### Anatomy
|
|
52
|
+
|
|
53
|
+
| Part | Required? | What it is |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| Root wrapper | Yes | `joy-input-unit` container around the inner input |
|
|
56
|
+
| Inner `VJoyInput` | Yes | Handles label, field chrome, validation styling, and the native `<input>` |
|
|
57
|
+
| Label | No | `VJoyLabel` from the `label` prop (or deprecated default slot) |
|
|
58
|
+
| Native `<input>` | Yes | Form control; `type` defaults to `number`; receives forwarded attrs |
|
|
59
|
+
| Unit badge | No | Pill on the right (`unit` prop); hidden when `unit` is an empty string |
|
|
60
|
+
| Leading icon | No | Decorative `VJoyIcon` on the left (`icon` prop) |
|
|
61
|
+
| Focus ring | Yes | Applied on focus via Joy form-field tokens — do not remove in product CSS |
|
|
62
|
+
|
|
63
|
+
The unit renders in the `right-action` slot of the inner input. It is **presentational** — the value in `v-model` does not include the unit string.
|
|
64
|
+
|
|
65
|
+
### Default values
|
|
66
|
+
|
|
67
|
+
- `type` defaults to **`number`**
|
|
68
|
+
- `unit` defaults to **`€`**
|
|
69
|
+
- `name` defaults to **`joy-input-number`**
|
|
70
|
+
- `size` defaults to **`medium`** (inherited from inner `VJoyInput`)
|
|
71
|
+
- `labelSize` defaults to **`medium`**
|
|
72
|
+
- `modelModifiers` defaults to **`{ lazy: false }`** (updates on `input`)
|
|
73
|
+
- `disabled`, `invalid`, `required`, `readonly`, `requiredMark`, and `autofocus` default to **`false`**
|
|
74
|
+
|
|
75
|
+
Props, events, and slots: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Variants
|
|
80
|
+
|
|
81
|
+
`VJoyInputNumber` has no visual “variant” prop. Visual meaning comes from **state** (`invalid`, `disabled`, `readonly`) and **density** (`size`).
|
|
82
|
+
|
|
83
|
+
### Default (valid)
|
|
84
|
+
|
|
85
|
+
Use for standard editable numeric fields. Border uses neutral tokens; focus uses `--joy-color-state-focus`.
|
|
86
|
+
|
|
87
|
+
### Invalid
|
|
88
|
+
|
|
89
|
+
Set `invalid` when server-side or client-side validation failed. Pair with `VJoyFormError` (or equivalent) so the error is announced — the red border alone is not enough.
|
|
90
|
+
|
|
91
|
+
Avoid toggling `invalid` while the user is still typing unless the value is clearly wrong (e.g. empty required field on blur).
|
|
92
|
+
|
|
93
|
+
### Readonly
|
|
94
|
+
|
|
95
|
+
Use when the value must remain visible but not editable (e.g. prefilled quote amount). Prefer readonly over disabled when the user should still read and copy the value.
|
|
96
|
+
|
|
97
|
+
### Disabled
|
|
98
|
+
|
|
99
|
+
Use when the field cannot be edited and the reason is external (permissions, workflow step). **Always explain why** nearby.
|
|
100
|
+
|
|
101
|
+
### Not in the API: `clearable`
|
|
102
|
+
|
|
103
|
+
`VJoyInputNumber` omits `clearable` and `clearAriaLabel` from its props. Attributes with those names are **ignored** — do not rely on a clear button. Use `VJoyInput` with `type="number"` when a clear action is required.
|
|
104
|
+
|
|
105
|
+
### Not in the API: custom unit slot
|
|
106
|
+
|
|
107
|
+
The unit is controlled only by the `unit` string prop. There is no slot to replace the unit badge with custom markup.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Sizes
|
|
112
|
+
|
|
113
|
+
Choose size for **density** within the form. Field heights come from `--joy-form-field-height-*`; the unit badge scales with the field via `--input-unit-height` — **do not hardcode pixel heights** in apps.
|
|
114
|
+
|
|
115
|
+
| Size | Use when |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| `small` | Dense toolbars, filter bars, inline numeric filters |
|
|
118
|
+
| `medium` | Default product forms |
|
|
119
|
+
| `large` | Marketing-scale or hero forms where fields need more prominence |
|
|
120
|
+
|
|
121
|
+
Match label size to field size when possible (`labelSize` aligns with `VJoyLabel` sizes).
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## States
|
|
126
|
+
|
|
127
|
+
| State | What the user sees / can do |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| Default | Empty or filled; editable; neutral border; unit badge visible when `unit` is non-empty |
|
|
130
|
+
| Hover | Border shifts to `--joy-color-state-hover` (pointer devices) |
|
|
131
|
+
| Focus | Focus border `--joy-color-state-focus`; wrapper gets `joy-input--focusing` |
|
|
132
|
+
| Valid (implicit) | `joy-input--valid` when not `invalid` and not disabled |
|
|
133
|
+
| Invalid | Error border and text color |
|
|
134
|
+
| Disabled | Not interactive; muted styling; explain why nearby |
|
|
135
|
+
| Readonly | Value visible; no edit; no focus styling for readonly |
|
|
136
|
+
| Empty unit | `unit=""` hides the unit badge; input behaves as a number field without suffix |
|
|
137
|
+
|
|
138
|
+
Mouse-wheel does not change the value while a `type="number"` field is focused (inherited from `VJoyInput`).
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Best practices
|
|
143
|
+
|
|
144
|
+
Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
|
|
145
|
+
|
|
146
|
+
### Labels and forms
|
|
147
|
+
|
|
148
|
+
- **MUST** provide a visible label for every field (via `label`, `VJoyLabel`, or an associated `<label for="…">`).
|
|
149
|
+
- **MUST** set `name` (and `id` when multiple fields share a form) so labels, autofill, and tests bind correctly. `id` falls back to `name`.
|
|
150
|
+
- **SHOULD** use `requiredMark` together with `required` when the asterisk pattern is used in the product.
|
|
151
|
+
- **SHOULD** use `optionalLabel` on non-required fields when the form mixes required and optional inputs.
|
|
152
|
+
- **SHOULD** wrap related fields in `VJoyWrapper` for spacing.
|
|
153
|
+
|
|
154
|
+
### Unit and value
|
|
155
|
+
|
|
156
|
+
- **MUST** set `unit` to the correct suffix for the field (`€`, `%`, `days`, …) — do not bake the unit into the label when the badge already shows it.
|
|
157
|
+
- **SHOULD** use `unit=""` only when you need this component’s number behaviour without a visible suffix.
|
|
158
|
+
- **MUST NOT** store the unit in `v-model` — the model is the numeric string only; formatting with unit belongs in display/submit logic.
|
|
159
|
+
- **SHOULD** pass `min`, `max`, and `step` via native attrs when the allowed range is fixed.
|
|
160
|
+
|
|
161
|
+
### Implementation
|
|
162
|
+
|
|
163
|
+
- **MUST** use only documented prop / enum values from MCP or `JoyInput.types.ts` (plus `unit` on this component).
|
|
164
|
+
- **MUST** bind value with `v-model` (`modelValue` / `update:modelValue`).
|
|
165
|
+
- **MUST** style with Joy tokens — no custom `.joy-input` or `.joy-input-unit` chrome.
|
|
166
|
+
- **MUST NOT** invent props (`clearable`, `variant`, `errorMessage`, …).
|
|
167
|
+
- **MUST NOT** pass `clearable` or `clearAriaLabel` — they are not supported.
|
|
168
|
+
- **MUST NOT** use `VJoyInput` with a `unit` prop — use `VJoyInputNumber` (enforced by `no-vjoyinput-unit` ESLint rule).
|
|
169
|
+
- **SHOULD** use `v-model.lazy` (via `modelModifiers: { lazy: true }`) when updates on every keystroke are expensive.
|
|
170
|
+
|
|
171
|
+
### Validation and errors
|
|
172
|
+
|
|
173
|
+
- **MUST** set `invalid` when showing a validation error for this field.
|
|
174
|
+
- **MUST** show the error message in `VJoyFormError` (or equivalent) below the field.
|
|
175
|
+
- **MUST NOT** rely on placeholder color alone to convey errors.
|
|
176
|
+
|
|
177
|
+
### Do / don’t
|
|
178
|
+
|
|
179
|
+
| Do | Don’t |
|
|
180
|
+
| --- | --- |
|
|
181
|
+
| `label="Daily rate"` + `unit="€"` | `label="Daily rate (€)"` when the badge shows € |
|
|
182
|
+
| `VJoyInputNumber` for priced amounts | `VJoyInput` + `unit` (removed) |
|
|
183
|
+
| `min` / `max` / `step` via native attrs | Custom spin buttons or ad-hoc unit markup |
|
|
184
|
+
| `VJoyFormError` with `invalid` | Red border with no message |
|
|
185
|
+
| `unit=""` when no suffix is needed | Dummy unit just to use the component |
|
|
186
|
+
| Joy form-field size tokens | Hardcoded `32` / `40` / `56`px heights |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Content
|
|
191
|
+
|
|
192
|
+
Inputs expose label, placeholder, optional helper copy, and a unit suffix. All must help users enter the **right numeric value** the **first** time.
|
|
193
|
+
|
|
194
|
+
### Label formula
|
|
195
|
+
|
|
196
|
+
**Noun or short noun phrase** describing the quantity — **without** repeating the unit shown in the badge:
|
|
197
|
+
|
|
198
|
+
- ✅ Daily rate (with `unit="€"`)
|
|
199
|
+
- ✅ Discount (with `unit="%"`)
|
|
200
|
+
- ✅ Notice period (with `unit="days"`)
|
|
201
|
+
|
|
202
|
+
Use sentence case. The unit badge carries the suffix; the label names what is being measured.
|
|
203
|
+
|
|
204
|
+
### Placeholder formula
|
|
205
|
+
|
|
206
|
+
**Example or format hint** — not a repeat of the label or unit:
|
|
207
|
+
|
|
208
|
+
- ✅ `1200` (under label “Daily rate” with `unit="€"`)
|
|
209
|
+
- ✅ `0` (under label “Discount” with `unit="%"`)
|
|
210
|
+
|
|
211
|
+
- ❌ `€` or `1200 €` as placeholder when the unit badge already shows €
|
|
212
|
+
|
|
213
|
+
### Unit copy
|
|
214
|
+
|
|
215
|
+
| Strength | Rule |
|
|
216
|
+
| --- | --- |
|
|
217
|
+
| **MUST** | Use standard symbols users recognize (`€`, `%`, `kg`) or short approved abbreviations (`days`, `h`) |
|
|
218
|
+
| **MUST NOT** | Put long explanations in `unit` — use helper text or `VJoyLabel` legend |
|
|
219
|
+
| **SHOULD** | Stay consistent with locale (currency symbol, `%` placement) across the product |
|
|
220
|
+
| **SHOULD** | Omit the badge (`unit=""`) when the unit is obvious from context and would clutter the field |
|
|
221
|
+
|
|
222
|
+
### Optional / required markers
|
|
223
|
+
|
|
224
|
+
| Pattern | When |
|
|
225
|
+
| --- | --- |
|
|
226
|
+
| `required` + `requiredMark` | Required field in a form that uses asterisks |
|
|
227
|
+
| `optionalLabel` (e.g. “optional”) | Field is not required among mostly required fields |
|
|
228
|
+
|
|
229
|
+
### Writing rules
|
|
230
|
+
|
|
231
|
+
| Strength | Rule |
|
|
232
|
+
| --- | --- |
|
|
233
|
+
| **MUST** | Every field has a visible label — never placeholder-only |
|
|
234
|
+
| **MUST** | Error messages state what went wrong and how to fix it — ✅ `Enter an amount between 1 and 999` · ❌ `Invalid` |
|
|
235
|
+
| **MUST NOT** | Duplicate the unit in the label when `unit` is set |
|
|
236
|
+
| **MUST NOT** | Use labels that only make sense after failure — ❌ `Correct amount` |
|
|
237
|
+
| **SHOULD** | Keep labels ≤ 3–5 words when possible |
|
|
238
|
+
| **SHOULD** | Use placeholders for example values, not requirements |
|
|
239
|
+
| **SHOULD** | Match terminology with adjacent fields and page headings |
|
|
240
|
+
| **SHOULD** (FR) | Follow French grammar for labels and unit symbols when the UI is localized |
|
|
241
|
+
|
|
242
|
+
### Patterns
|
|
243
|
+
|
|
244
|
+
**Money** — Label names the amount type (`Price`, `Daily rate`); `unit` holds the currency. Formatting (decimals, thousands) is an app concern, not the badge.
|
|
245
|
+
|
|
246
|
+
**Percentages** — Label describes what is measured (`Discount`, `Commission`); `unit="%"`. Consider `min="0"` `max="100"` via attrs when appropriate.
|
|
247
|
+
|
|
248
|
+
**Durations / counts** — Short unit strings (`days`, `h`, `mo`) in `unit`; label states what is counted (`Notice period`, `Experience`).
|
|
249
|
+
|
|
250
|
+
**Errors** — Show on blur or submit for most fields; pair `invalid` with a specific `VJoyFormError` message.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Accessibility
|
|
255
|
+
|
|
256
|
+
Behaviors (what must happen), not slogans.
|
|
257
|
+
|
|
258
|
+
| Behavior | Expectation |
|
|
259
|
+
| --- | --- |
|
|
260
|
+
| Role | Native `<input>` with `type="number"` by default (overridable via `type`) |
|
|
261
|
+
| Name | From associated `VJoyLabel` / `<label for>`; `label` prop wires `for` to `id \|\| name` |
|
|
262
|
+
| Value | Controlled via `v-model`; emits string updates |
|
|
263
|
+
| Unit | Visual suffix; not a substitute for the accessible name — label must still describe the field |
|
|
264
|
+
| Keyboard | Standard editing; Tab moves focus; number spinners follow browser defaults |
|
|
265
|
+
| Focus | Visible focus ring on the input — do not remove focus tokens |
|
|
266
|
+
| Wheel | Wheel does not change value while focused (`type="number"`) |
|
|
267
|
+
| Invalid | `invalid` styles the field; pair with `VJoyFormError` (`role="alert"`) for the message |
|
|
268
|
+
| Disabled | Native `disabled`; provide text explanation nearby |
|
|
269
|
+
| Readonly | Native `readonly`; value still readable and copyable |
|
|
270
|
+
| Programmatic focus | Exposed `focus()` delegates to the inner input |
|
|
271
|
+
|
|
272
|
+
Target: **WCAG 2.2 AA** where the product requires it.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Examples
|
|
277
|
+
|
|
278
|
+
```vue
|
|
279
|
+
<script setup lang="ts">
|
|
280
|
+
import { ref } from 'vue'
|
|
281
|
+
import { VJoyInputNumber, VJoyFormError, VJoyWrapper } from '@maltjoy/core-vue'
|
|
282
|
+
|
|
283
|
+
const price = ref('120')
|
|
284
|
+
const discount = ref('')
|
|
285
|
+
const showError = ref(false)
|
|
286
|
+
</script>
|
|
287
|
+
|
|
288
|
+
<template>
|
|
289
|
+
<!-- Price with default unit (€) -->
|
|
290
|
+
<VJoyInputNumber
|
|
291
|
+
v-model="price"
|
|
292
|
+
name="daily-rate"
|
|
293
|
+
label="Daily rate"
|
|
294
|
+
placeholder="1200"
|
|
295
|
+
min="0"
|
|
296
|
+
step="1"
|
|
297
|
+
required
|
|
298
|
+
required-mark
|
|
299
|
+
/>
|
|
300
|
+
|
|
301
|
+
<!-- Custom unit -->
|
|
302
|
+
<VJoyInputNumber
|
|
303
|
+
v-model="discount"
|
|
304
|
+
name="discount"
|
|
305
|
+
label="Discount"
|
|
306
|
+
unit="%"
|
|
307
|
+
placeholder="0"
|
|
308
|
+
min="0"
|
|
309
|
+
max="100"
|
|
310
|
+
:invalid="showError"
|
|
311
|
+
/>
|
|
312
|
+
<VJoyFormError v-if="showError" message="Enter a value between 0 and 100" />
|
|
313
|
+
|
|
314
|
+
<!-- No visible unit suffix -->
|
|
315
|
+
<VJoyInputNumber
|
|
316
|
+
v-model="price"
|
|
317
|
+
name="quantity"
|
|
318
|
+
label="Quantity"
|
|
319
|
+
unit=""
|
|
320
|
+
min="1"
|
|
321
|
+
/>
|
|
322
|
+
|
|
323
|
+
<!-- Form layout -->
|
|
324
|
+
<VJoyWrapper direction="column" row-gap="4">
|
|
325
|
+
<VJoyInputNumber
|
|
326
|
+
v-model="price"
|
|
327
|
+
name="project-budget"
|
|
328
|
+
label="Project budget"
|
|
329
|
+
unit="€"
|
|
330
|
+
size="medium"
|
|
331
|
+
/>
|
|
332
|
+
</VJoyWrapper>
|
|
333
|
+
</template>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
In Nuxt (or apps with auto-import), omit the import if `VJoyInputNumber` is already global.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Related
|
|
341
|
+
|
|
342
|
+
| Component | Use when |
|
|
343
|
+
| --- | --- |
|
|
344
|
+
| `VJoyInput` | Free text or plain `type="number"` without unit badge (supports `clearable`) |
|
|
345
|
+
| `VJoyLabel` | Rich labels (legend, tooltip) separate from the field |
|
|
346
|
+
| `VJoyFormError` | Inline validation message below the field |
|
|
347
|
+
| `VJoyWrapper` | Form layout and spacing between fields |
|
|
348
|
+
| `VJoyInputPassword` | Password entry |
|
|
349
|
+
| `VJoyTextarea` | Multi-line text |
|
|
350
|
+
| `VJoySelect` | Single choice from a list |
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## For agents
|
|
355
|
+
|
|
356
|
+
Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoyInputNumber.dsds.json`](./JoyInputNumber.dsds.json).
|
|
357
|
+
|
|
358
|
+
### Pick the right component
|
|
359
|
+
|
|
360
|
+
| If the intent is… | Emit… | Do not emit… |
|
|
361
|
+
| --- | --- | --- |
|
|
362
|
+
| Numeric value + unit suffix (€, %, days) | `VJoyInputNumber` | `VJoyInput` + `unit` |
|
|
363
|
+
| Plain number, no unit badge | `VJoyInput` + `type="number"` | `VJoyInputNumber` with fake unit |
|
|
364
|
+
| Number + clear button | `VJoyInput` + `type="number"` + `clearable` | `VJoyInputNumber` + `clearable` |
|
|
365
|
+
| Free text | `VJoyInput` | `VJoyInputNumber` |
|
|
366
|
+
| Password | `VJoyInputPassword` | `VJoyInput` + `type="password"` |
|
|
367
|
+
| Pick one of N options | `VJoySelect` / radios | Number input for enums |
|
|
368
|
+
|
|
369
|
+
### Hard rules
|
|
370
|
+
|
|
371
|
+
1. **MUST** call `joy_get_component_info("VJoyInputNumber")` or read `VJoyInputNumber.vue` / `JoyInput.types.ts` before generating props.
|
|
372
|
+
2. **MUST** bind `v-model` for editable values.
|
|
373
|
+
3. **MUST** set `name` (and `id` when needed for label association).
|
|
374
|
+
4. **MUST** set `unit` to the correct suffix; use `unit=""` when no badge is wanted.
|
|
375
|
+
5. **MUST** pass native attrs (`placeholder`, `min`, `max`, `step`, `aria-*`) on the component — they flow to `<input>`.
|
|
376
|
+
6. **MUST** set `invalid` when showing validation errors and render `VJoyFormError` with a message.
|
|
377
|
+
7. **MUST NOT** emit `unit` on `VJoyInput` — use `VJoyInputNumber`.
|
|
378
|
+
8. **MUST NOT** emit `clearable` or `clearAriaLabel` on `VJoyInputNumber` — not supported.
|
|
379
|
+
9. **MUST NOT** use the default slot for new labels — use `label` or `VJoyLabel`.
|
|
380
|
+
10. **MUST NOT** override `.joy-input` / `.joy-input-unit` height, border, radius, or padding.
|
|
381
|
+
11. **SHOULD** default `type` to `number`; override only when a documented use case requires it.
|
|
382
|
+
12. **SHOULD** use `VJoyWrapper` in multi-field forms.
|
|
383
|
+
|
|
384
|
+
### Checklist
|
|
385
|
+
|
|
386
|
+
- [ ] Correct component (`VJoyInputNumber` vs `VJoyInput` / alternatives)
|
|
387
|
+
- [ ] Props / enums from MCP or types only
|
|
388
|
+
- [ ] `v-model` + `name` present
|
|
389
|
+
- [ ] Visible label (not placeholder-only)
|
|
390
|
+
- [ ] `unit` set appropriately; not duplicated in label
|
|
391
|
+
- [ ] `invalid` + `VJoyFormError` when validation fails
|
|
392
|
+
- [ ] No `clearable` / `clearAriaLabel`
|
|
393
|
+
- [ ] No invented props / no custom input chrome CSS
|
|
394
|
+
- [ ] Disabled / readonly explained if user-visible
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## DSDS mapping
|
|
399
|
+
|
|
400
|
+
This Markdown mirrors [`JoyInputNumber.dsds.json`](./JoyInputNumber.dsds.json) (DSDS **0.15.2**).
|
|
401
|
+
|
|
402
|
+
| Section here | DSDS block |
|
|
403
|
+
| --- | --- |
|
|
404
|
+
| When to use / not | `use-cases` |
|
|
405
|
+
| Anatomy | `anatomy` |
|
|
406
|
+
| Sizes / states | `variants` |
|
|
407
|
+
| States | `states` |
|
|
408
|
+
| Best practices / content rules | `guidelines` (+ `content`) |
|
|
409
|
+
| Accessibility | `accessibility` |
|
|
410
|
+
| Examples | `sections` |
|
|
411
|
+
| For agents | `agentDocumentBlocks` |
|
|
412
|
+
| Props detail | `api` in JSON + Joy MCP |
|
|
413
|
+
| Imports | `imports` in JSON |
|
|
414
|
+
| Token sizing | `design-specifications` in JSON (`--joy-form-field-height-*`, `--input-unit-height`) |
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"type": "string | undefined",
|
|
30
30
|
"required": false,
|
|
31
31
|
"default": "\"number\"",
|
|
32
|
-
"description": "",
|
|
32
|
+
"description": "HTML input type. Use VJoyInputPassword for password fields.",
|
|
33
33
|
"possibleValues": null
|
|
34
34
|
},
|
|
35
35
|
{
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"name": "name",
|
|
86
86
|
"type": "string | undefined",
|
|
87
87
|
"required": false,
|
|
88
|
-
"default": "\"joy-input-
|
|
88
|
+
"default": "\"joy-input-number\"",
|
|
89
89
|
"description": "",
|
|
90
90
|
"possibleValues": [
|
|
91
91
|
"string"
|
|
@@ -155,16 +155,6 @@
|
|
|
155
155
|
"Partial<Record<\"number\" | \"trim\" | \"lazy\", boolean>>"
|
|
156
156
|
]
|
|
157
157
|
},
|
|
158
|
-
{
|
|
159
|
-
"name": "unit",
|
|
160
|
-
"type": "string | undefined",
|
|
161
|
-
"required": false,
|
|
162
|
-
"default": "\"\\u20AC\"",
|
|
163
|
-
"description": "Adds a legend like symbol on the right of the component. Made for number based values",
|
|
164
|
-
"possibleValues": [
|
|
165
|
-
"string"
|
|
166
|
-
]
|
|
167
|
-
},
|
|
168
158
|
{
|
|
169
159
|
"name": "readonly",
|
|
170
160
|
"type": "boolean | undefined",
|
|
@@ -175,6 +165,16 @@
|
|
|
175
165
|
"false",
|
|
176
166
|
"true"
|
|
177
167
|
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"name": "unit",
|
|
171
|
+
"type": "string | undefined",
|
|
172
|
+
"required": false,
|
|
173
|
+
"default": "\"\\u20AC\"",
|
|
174
|
+
"description": "Unit label displayed on the right side of the number input.",
|
|
175
|
+
"possibleValues": [
|
|
176
|
+
"string"
|
|
177
|
+
]
|
|
178
178
|
}
|
|
179
179
|
],
|
|
180
180
|
"events": [
|
|
@@ -184,7 +184,12 @@
|
|
|
184
184
|
"signature": "(event: \"update:modelValue\", value: string): void"
|
|
185
185
|
}
|
|
186
186
|
],
|
|
187
|
-
"slots": [
|
|
187
|
+
"slots": [
|
|
188
|
+
{
|
|
189
|
+
"name": "default",
|
|
190
|
+
"description": ""
|
|
191
|
+
}
|
|
192
|
+
],
|
|
188
193
|
"exposed": [
|
|
189
194
|
{
|
|
190
195
|
"name": "invalid",
|
|
@@ -232,13 +237,13 @@
|
|
|
232
237
|
"isMethod": false
|
|
233
238
|
},
|
|
234
239
|
{
|
|
235
|
-
"name": "
|
|
236
|
-
"type": "
|
|
240
|
+
"name": "readonly",
|
|
241
|
+
"type": "boolean",
|
|
237
242
|
"isMethod": false
|
|
238
243
|
},
|
|
239
244
|
{
|
|
240
|
-
"name": "
|
|
241
|
-
"type": "
|
|
245
|
+
"name": "unit",
|
|
246
|
+
"type": "string",
|
|
242
247
|
"isMethod": false
|
|
243
248
|
},
|
|
244
249
|
{
|