nuxt-ui-formwerk 0.1.3 → 0.1.5
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/README.md +88 -55
- package/dist/module.json +1 -1
- package/dist/module.mjs +3 -0
- package/dist/runtime/components/Field.d.vue.ts +1 -1
- package/dist/runtime/components/Field.vue +24 -4
- package/dist/runtime/components/Field.vue.d.ts +1 -1
- package/dist/runtime/components/Form.vue +14 -2
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
[![License][license-src]][license-href]
|
|
6
6
|
[![Nuxt][nuxt-src]][nuxt-href]
|
|
7
7
|
|
|
8
|
+
> [!WARNING]
|
|
9
|
+
> **⚠️ Experimental Module**
|
|
10
|
+
>
|
|
11
|
+
> This module is currently in an experimental phase. APIs may change, and some features may not be fully stable. Use with caution in production environments and please report any issues you encounter.
|
|
12
|
+
>
|
|
13
|
+
> **Technical Note:** This module works by tapping into Nuxt UI's form component injection mechanism and intercepting bus events to coordinate between Nuxt UI's native form system and formwerk's validation engine. Changes to Nuxt UI's internal form architecture may require updates to this module.
|
|
14
|
+
|
|
8
15
|
Enhanced form components for Nuxt UI with [@formwerk/core](https://formwerk.dev/) integration. This module bridges the gap between Formwerk's powerful form validation and state management with Nuxt UI's beautiful form components.
|
|
9
16
|
|
|
10
17
|
- [✨ Release Notes](/CHANGELOG.md)
|
|
@@ -31,8 +38,8 @@ Add the module to your `nuxt.config.ts`:
|
|
|
31
38
|
|
|
32
39
|
```ts
|
|
33
40
|
export default defineNuxtConfig({
|
|
34
|
-
modules: [
|
|
35
|
-
})
|
|
41
|
+
modules: ["@nuxt/ui", "nuxt-ui-formwerk"],
|
|
42
|
+
});
|
|
36
43
|
```
|
|
37
44
|
|
|
38
45
|
That's it! You can now use enhanced form components in your Nuxt app ✨
|
|
@@ -47,21 +54,26 @@ The root form component that provides validation context and tracks form state.
|
|
|
47
54
|
|
|
48
55
|
```vue
|
|
49
56
|
<script setup lang="ts">
|
|
50
|
-
import { z } from
|
|
57
|
+
import { z } from "zod";
|
|
51
58
|
|
|
52
59
|
const schema = z.object({
|
|
53
60
|
email: z.string().email(),
|
|
54
|
-
password: z.string().min(8)
|
|
55
|
-
})
|
|
61
|
+
password: z.string().min(8),
|
|
62
|
+
});
|
|
56
63
|
|
|
57
64
|
const state = reactive({
|
|
58
|
-
email:
|
|
59
|
-
password:
|
|
60
|
-
})
|
|
65
|
+
email: "",
|
|
66
|
+
password: "",
|
|
67
|
+
});
|
|
61
68
|
</script>
|
|
62
69
|
|
|
63
70
|
<template>
|
|
64
|
-
<FormwerkForm
|
|
71
|
+
<FormwerkForm
|
|
72
|
+
:schema="schema"
|
|
73
|
+
:state="state"
|
|
74
|
+
validate-on="blur"
|
|
75
|
+
#="{ blurredFields, touchedFields, dirtyFields }"
|
|
76
|
+
>
|
|
65
77
|
<!-- Form content here -->
|
|
66
78
|
<p>Blurred fields: {{ blurredFields.size }}</p>
|
|
67
79
|
</FormwerkForm>
|
|
@@ -70,10 +82,10 @@ const state = reactive({
|
|
|
70
82
|
|
|
71
83
|
#### Props
|
|
72
84
|
|
|
73
|
-
| Prop | Type
|
|
74
|
-
| ------------ |
|
|
75
|
-
| `validateOn` | `'touched' \| 'blur' \| 'dirty'` | `'blur'` | When to trigger validation
|
|
76
|
-
| `disabled` | `boolean`
|
|
85
|
+
| Prop | Type | Default | Description |
|
|
86
|
+
| ------------ | -------------------------------- | -------- | -------------------------- |
|
|
87
|
+
| `validateOn` | `'touched' \| 'blur' \| 'dirty'` | `'blur'` | When to trigger validation |
|
|
88
|
+
| `disabled` | `boolean` | `false` | Disable all form fields |
|
|
77
89
|
|
|
78
90
|
#### Slot Props
|
|
79
91
|
|
|
@@ -137,43 +149,64 @@ Groups related form fields together for nested validation.
|
|
|
137
149
|
|
|
138
150
|
```vue
|
|
139
151
|
<script setup lang="ts">
|
|
140
|
-
import { z } from
|
|
152
|
+
import { z } from "zod";
|
|
141
153
|
|
|
142
154
|
const schema = z.object({
|
|
143
|
-
name: z.string().min(2,
|
|
144
|
-
email: z.string().email(
|
|
145
|
-
password: z.string().min(8,
|
|
146
|
-
})
|
|
155
|
+
name: z.string().min(2, "Name must be at least 2 characters"),
|
|
156
|
+
email: z.string().email("Invalid email address"),
|
|
157
|
+
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
158
|
+
});
|
|
147
159
|
|
|
148
160
|
const state = reactive({
|
|
149
|
-
name:
|
|
150
|
-
email:
|
|
151
|
-
password:
|
|
152
|
-
})
|
|
161
|
+
name: "",
|
|
162
|
+
email: "",
|
|
163
|
+
password: "",
|
|
164
|
+
});
|
|
153
165
|
|
|
154
166
|
const onSubmit = () => {
|
|
155
|
-
console.log(
|
|
156
|
-
}
|
|
167
|
+
console.log("Form submitted:", state);
|
|
168
|
+
};
|
|
157
169
|
</script>
|
|
158
170
|
|
|
159
171
|
<template>
|
|
160
|
-
<FormwerkForm
|
|
172
|
+
<FormwerkForm
|
|
173
|
+
:schema="schema"
|
|
174
|
+
:state="state"
|
|
175
|
+
validate-on="blur"
|
|
176
|
+
#="{ blurredFields }"
|
|
177
|
+
>
|
|
161
178
|
<div class="space-y-4">
|
|
162
179
|
<FormwerkField name="name" label="Name" required #="{ setValue, value }">
|
|
163
180
|
<UInput :model-value="value" @update:model-value="setValue" />
|
|
164
181
|
</FormwerkField>
|
|
165
182
|
|
|
166
|
-
<FormwerkField
|
|
167
|
-
|
|
183
|
+
<FormwerkField
|
|
184
|
+
name="email"
|
|
185
|
+
label="Email"
|
|
186
|
+
required
|
|
187
|
+
#="{ setValue, value }"
|
|
188
|
+
>
|
|
189
|
+
<UInput
|
|
190
|
+
:model-value="value"
|
|
191
|
+
@update:model-value="setValue"
|
|
192
|
+
type="email"
|
|
193
|
+
/>
|
|
168
194
|
</FormwerkField>
|
|
169
195
|
|
|
170
|
-
<FormwerkField
|
|
171
|
-
|
|
196
|
+
<FormwerkField
|
|
197
|
+
name="password"
|
|
198
|
+
label="Password"
|
|
199
|
+
required
|
|
200
|
+
#="{ setValue, value }"
|
|
201
|
+
>
|
|
202
|
+
<UInput
|
|
203
|
+
:model-value="value"
|
|
204
|
+
@update:model-value="setValue"
|
|
205
|
+
type="password"
|
|
206
|
+
/>
|
|
172
207
|
</FormwerkField>
|
|
173
208
|
|
|
174
|
-
<UButton type="submit" @click="onSubmit">
|
|
175
|
-
Submit
|
|
176
|
-
</UButton>
|
|
209
|
+
<UButton type="submit" @click="onSubmit"> Submit </UButton>
|
|
177
210
|
|
|
178
211
|
<p class="text-sm text-gray-500">
|
|
179
212
|
Fields blurred: {{ blurredFields.size }}
|
|
@@ -207,38 +240,38 @@ The integration allows you to use Nuxt UI's beautiful form components while leve
|
|
|
207
240
|
<details>
|
|
208
241
|
<summary>Local development</summary>
|
|
209
242
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
243
|
+
```bash
|
|
244
|
+
# Install dependencies
|
|
245
|
+
pnpm install
|
|
213
246
|
|
|
214
|
-
|
|
215
|
-
|
|
247
|
+
# Generate type stubs and prepare playground
|
|
248
|
+
pnpm dev:prepare
|
|
216
249
|
|
|
217
|
-
|
|
218
|
-
|
|
250
|
+
# Develop with the playground
|
|
251
|
+
pnpm dev
|
|
219
252
|
|
|
220
|
-
|
|
221
|
-
|
|
253
|
+
# Build the playground
|
|
254
|
+
pnpm dev:build
|
|
222
255
|
|
|
223
|
-
|
|
224
|
-
|
|
256
|
+
# Run linter (oxlint)
|
|
257
|
+
pnpm lint
|
|
225
258
|
|
|
226
|
-
|
|
227
|
-
|
|
259
|
+
# Fix linting issues
|
|
260
|
+
pnpm lint:fix
|
|
228
261
|
|
|
229
|
-
|
|
230
|
-
|
|
262
|
+
# Format code (oxfmt)
|
|
263
|
+
pnpm format
|
|
231
264
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
265
|
+
# Run tests
|
|
266
|
+
pnpm test
|
|
267
|
+
pnpm test:watch
|
|
235
268
|
|
|
236
|
-
|
|
237
|
-
|
|
269
|
+
# Type check
|
|
270
|
+
pnpm test:types
|
|
238
271
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
272
|
+
# Release new version
|
|
273
|
+
pnpm release
|
|
274
|
+
```
|
|
242
275
|
|
|
243
276
|
</details>
|
|
244
277
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import {} from "@nuxt/ui";
|
|
2
3
|
import { useCustomControl } from "@formwerk/core";
|
|
3
|
-
import {
|
|
4
|
+
import { formBusInjectionKey } from "#imports";
|
|
5
|
+
import { inject, watch, computed } from "vue";
|
|
6
|
+
import {
|
|
7
|
+
formwerkOptionsInjectionKey,
|
|
8
|
+
formwerkBusInjectionKey
|
|
9
|
+
} from "./Form.vue";
|
|
4
10
|
const props = defineProps({
|
|
5
11
|
as: { type: null, required: false },
|
|
6
12
|
name: { type: String, required: false },
|
|
@@ -17,8 +23,21 @@ const formBus = inject(formBusInjectionKey, void 0);
|
|
|
17
23
|
const formwerkBus = inject(formwerkBusInjectionKey, void 0);
|
|
18
24
|
const formwerkOptions = inject(formwerkOptionsInjectionKey, void 0);
|
|
19
25
|
const {
|
|
20
|
-
field: {
|
|
21
|
-
|
|
26
|
+
field: {
|
|
27
|
+
errorMessage,
|
|
28
|
+
fieldValue,
|
|
29
|
+
setValue,
|
|
30
|
+
setBlurred,
|
|
31
|
+
setTouched,
|
|
32
|
+
isTouched,
|
|
33
|
+
isBlurred,
|
|
34
|
+
isDirty
|
|
35
|
+
}
|
|
36
|
+
} = useCustomControl({
|
|
37
|
+
name: props.name,
|
|
38
|
+
required: props.required,
|
|
39
|
+
disabled: formwerkOptions?.value?.disabled
|
|
40
|
+
});
|
|
22
41
|
const emitFormEvent = (type, name, payload) => {
|
|
23
42
|
if (formwerkBus && name) formwerkBus.emit(type, { name, payload });
|
|
24
43
|
};
|
|
@@ -26,7 +45,8 @@ watch(isTouched, (newValue) => emitFormEvent("touched", props.name, newValue));
|
|
|
26
45
|
watch(isBlurred, (newValue) => emitFormEvent("blur", props.name, newValue));
|
|
27
46
|
watch(isDirty, (newValue) => emitFormEvent("dirty", props.name, newValue));
|
|
28
47
|
const error = computed(() => {
|
|
29
|
-
if (!formwerkOptions || !formwerkOptions.value)
|
|
48
|
+
if (!formwerkOptions || !formwerkOptions.value)
|
|
49
|
+
return errorMessage.value ? errorMessage.value : void 0;
|
|
30
50
|
switch (formwerkOptions.value.validateOn) {
|
|
31
51
|
case "blur":
|
|
32
52
|
return isBlurred.value && errorMessage.value ? errorMessage.value : void 0;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import { provide, reactive, computed } from "vue";
|
|
3
|
+
import {
|
|
4
|
+
formBusInjectionKey,
|
|
5
|
+
formOptionsInjectionKey
|
|
6
|
+
} from "@nuxt/ui/composables/useFormField";
|
|
7
|
+
import { useEventBus } from "@vueuse/core";
|
|
2
8
|
import { useFormContext } from "@formwerk/core";
|
|
3
9
|
export const formwerkOptionsInjectionKey = /* @__PURE__ */ Symbol("nuxt-ui-formwerk.form-options");
|
|
4
10
|
export const formwerkBusInjectionKey = /* @__PURE__ */ Symbol("nuxt-ui-formwerk.form-events");
|
|
@@ -10,7 +16,9 @@ const { validateOn = "blur", disabled = false } = defineProps({
|
|
|
10
16
|
validateOn: { type: String, required: false },
|
|
11
17
|
disabled: { type: Boolean, required: false }
|
|
12
18
|
});
|
|
13
|
-
const formwerkBus = useEventBus(
|
|
19
|
+
const formwerkBus = useEventBus(
|
|
20
|
+
`formwerk-form-${context.id}`
|
|
21
|
+
);
|
|
14
22
|
const NuxtUiFormBus = useEventBus(`form-${context.id}`);
|
|
15
23
|
const dirtyFields = reactive(/* @__PURE__ */ new Set());
|
|
16
24
|
const touchedFields = reactive(/* @__PURE__ */ new Set());
|
|
@@ -55,6 +63,10 @@ formwerkBus.on(async (event, payload) => {
|
|
|
55
63
|
|
|
56
64
|
<template>
|
|
57
65
|
<div>
|
|
58
|
-
<slot
|
|
66
|
+
<slot
|
|
67
|
+
:blurred-fields="blurredFields"
|
|
68
|
+
:touched-fields="touchedFields"
|
|
69
|
+
:dirty-fields="dirtyFields"
|
|
70
|
+
/>
|
|
59
71
|
</div>
|
|
60
72
|
</template>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-ui-formwerk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A collection of beautiful, animated UI components for Nuxt applications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/genu/nuxt-ui-formwerk.git",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@nuxt/kit": "^4.2.2"
|
|
26
|
+
"@nuxt/kit": "^4.2.2",
|
|
27
|
+
"@vueuse/core": "^14.1.0"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@nuxt/devtools": "^3.1.1",
|
|
@@ -37,13 +38,14 @@
|
|
|
37
38
|
"oxfmt": "^0.20.0",
|
|
38
39
|
"oxlint": "^1.35.0",
|
|
39
40
|
"oxlint-tsgolint": "^0.10.0",
|
|
41
|
+
"prettier": "^3.7.4",
|
|
40
42
|
"typescript": "~5.9.3",
|
|
41
43
|
"vitest": "^4.0.16",
|
|
42
44
|
"vue-tsc": "^3.2.1"
|
|
43
45
|
},
|
|
44
46
|
"peerDependencies": {
|
|
45
|
-
"@
|
|
46
|
-
"@
|
|
47
|
+
"@formwerk/core": "^0.14.4",
|
|
48
|
+
"@nuxt/ui": "^4.0.0"
|
|
47
49
|
},
|
|
48
50
|
"scripts": {
|
|
49
51
|
"dev": "pnpm dev:prepare && nuxi dev playground",
|
|
@@ -51,8 +53,8 @@
|
|
|
51
53
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
52
54
|
"lint": "oxlint --type-aware",
|
|
53
55
|
"lint:fix": "oxlint --fix --type-aware",
|
|
54
|
-
"format": "
|
|
55
|
-
"format:check": "
|
|
56
|
+
"format": "prettier --write .",
|
|
57
|
+
"format:check": "prettier --check .",
|
|
56
58
|
"release": "pnpm lint && pnpm test && pnpm prepack && changelogen --release && pnpm publish && git push --follow-tags",
|
|
57
59
|
"test": "vitest run",
|
|
58
60
|
"test:watch": "vitest watch",
|