srcdev-nuxt-forms 2.0.2 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/composables/useZodValidation.ts +1 -0
  2. package/package.json +19 -2
  3. package/.editorconfig +0 -12
  4. package/.eslintignore +0 -7
  5. package/.eslintrc.cjs +0 -12
  6. package/.nmprc +0 -2
  7. package/.nuxtrc +0 -1
  8. package/.playground/components/scaffolding/footer/NavFooter.vue +0 -62
  9. package/.playground/components/ui/content-grid/ContentGrid.vue +0 -85
  10. package/.playground/composables/useApiRequest.ts +0 -25
  11. package/.playground/composables/useErrorMessages.ts +0 -59
  12. package/.playground/composables/useFormControl.ts +0 -248
  13. package/.playground/composables/useSleep.ts +0 -5
  14. package/.playground/composables/useStyleClassPassthrough.ts +0 -30
  15. package/.playground/composables/useZodValidation.ts +0 -120
  16. package/.playground/layouts/default.vue +0 -72
  17. package/.playground/nuxt.config.ts +0 -27
  18. package/.playground/pages/forms/examples/buttons/index.vue +0 -155
  19. package/.playground/pages/forms/examples/material/cssbattle.vue +0 -60
  20. package/.playground/pages/forms/examples/material/text-fields.vue +0 -594
  21. package/.playground/pages/index.vue +0 -33
  22. package/.playground/pages/limit-text.vue +0 -43
  23. package/.playground/pages/typography.vue +0 -83
  24. package/.playground/server/api/places/list.get.ts +0 -23
  25. package/.playground/server/api/textFields.post.ts +0 -37
  26. package/.playground/server/api/utils/index.get.ts +0 -20
  27. package/.playground/server/data/places/cities.json +0 -43
  28. package/.playground/server/data/places/countries.json +0 -55
  29. package/.playground/server/data/utils/title.json +0 -49
  30. package/.playground/types/types.places.ts +0 -8
  31. package/.prettierrc +0 -5
  32. package/.release-it.json +0 -6
  33. package/tsconfig.json +0 -3
  34. /package/{.playground/types → types}/types.forms.ts +0 -0
  35. /package/{.playground/types → types}/types.zodFormControl.ts +0 -0
@@ -1,120 +0,0 @@
1
- import { z, ZodError } from 'zod';
2
- import type { IFormFieldStateObj, ApiErrorResponse } from '@/types/types.forms';
3
-
4
- const useZodValidation = (formSchema: any) => {
5
- const zodFormControl = reactive({
6
- errorCount: 0,
7
- displayLoader: false,
8
- submitDisabled: false,
9
- submitAttempted: false,
10
- submitSuccessful: false,
11
- formIsValid: false,
12
- isPending: false,
13
- isDisabled: false,
14
- previousState: {} as Record<string, any>,
15
- });
16
-
17
- type formSchema = z.infer<typeof formSchema>;
18
- const zodErrorObj = ref<z.ZodFormattedError<formSchema> | null>(null);
19
-
20
- const resetPreviousValues = () => {
21
- for (const [field] of Object.entries(formSchema.shape)) {
22
- const previousValue = {
23
- value: null,
24
- message: '',
25
- };
26
-
27
- zodFormControl.previousState[field] = previousValue;
28
- }
29
- };
30
-
31
- const initZodForm = () => {
32
- resetPreviousValues();
33
- };
34
-
35
- const getErrorCount = (zodErrorObj: Ref<z.ZodFormattedError<formSchema> | null>) => {
36
- const zodCountErrors = zodErrorObj.value ?? [];
37
- // @ts-ignore
38
- delete zodCountErrors._errors;
39
- const errorCount = Object.keys(zodCountErrors ?? []).length;
40
- return errorCount;
41
- };
42
-
43
- const transformErrorMessages = (errors: any) => {
44
- const apiErrors = ref({}) as any;
45
- for (const [key, value] of Object.entries(errors)) {
46
- const fieldPath = key.split('.').map((key: string) => key.charAt(0).toLowerCase() + key.slice(1));
47
- apiErrors.value[fieldPath.join('.')] = value;
48
- }
49
-
50
- return apiErrors.value;
51
- };
52
-
53
- const updatePreviousValue = async (field: string, message: string, state: Record<string, any>) => {
54
- const previousValue = {
55
- value: state[field],
56
- message: message,
57
- };
58
- zodFormControl.previousState[field] = previousValue;
59
- };
60
-
61
- const pushCustomErrors = async (apiErrorResponse: ApiErrorResponse, state: Record<string, any>) => {
62
- const apiErrors = transformErrorMessages(apiErrorResponse.data.errors);
63
-
64
- // 1: Create a ZodError object to hold the issues
65
- const zodError = new ZodError([]);
66
-
67
- // 2: Reset previous state values
68
- resetPreviousValues();
69
-
70
- // 3: Add issues to the ZodError object
71
- for (const [path, message] of Object.entries(apiErrors)) {
72
- zodError.addIssue({
73
- path: path.split('.'),
74
- message: message as string,
75
- code: z.ZodIssueCode.custom,
76
- });
77
- await updatePreviousValue(path, message as string, state);
78
- }
79
-
80
- zodErrorObj.value = zodError.format();
81
- zodFormControl.errorCount = getErrorCount(zodErrorObj);
82
- zodFormControl.formIsValid = zodFormControl.errorCount === 0;
83
- zodFormControl.displayLoader = false;
84
- zodFormControl.submitAttempted = true;
85
- return zodErrorObj.value;
86
- };
87
-
88
- const doZodValidate = async (state: Record<string, any>) => {
89
- const valid = formSchema.safeParse(toRaw(state));
90
- if (!valid.success) {
91
- zodErrorObj.value = valid.error.format();
92
- } else {
93
- zodErrorObj.value = null;
94
- }
95
-
96
- zodFormControl.errorCount = getErrorCount(zodErrorObj);
97
- zodFormControl.formIsValid = valid.success;
98
-
99
- return valid.success;
100
- };
101
-
102
- const fieldMaxLength = (name: string) => {
103
- const fieldSchema = formSchema.shape[name];
104
- if (fieldSchema instanceof z.ZodString) {
105
- return fieldSchema._def.checks.find((check) => check.kind === 'max')?.value || null;
106
- }
107
- return null;
108
- };
109
-
110
- return {
111
- initZodForm,
112
- zodFormControl,
113
- zodErrorObj,
114
- pushCustomErrors,
115
- doZodValidate,
116
- fieldMaxLength,
117
- };
118
- };
119
-
120
- export default useZodValidation;
@@ -1,72 +0,0 @@
1
- <template>
2
- <div class="page-layout">
3
- <div>
4
- <h1><NuxtLink to="/">Home</NuxtLink></h1>
5
- <ul class="flex-group">
6
- <li>
7
- <NuxtLink to="/typography" class="link-normal">Typography</NuxtLink>
8
- </li>
9
- <li>
10
- <NuxtLink to="/forms/examples/material/text-fields" class="link-normal">Material UI text fields</NuxtLink>
11
- </li>
12
- <li>
13
- <NuxtLink to="/forms/examples/buttons" class="link-normal">Buttons</NuxtLink>
14
- </li>
15
- </ul>
16
- </div>
17
-
18
- <div class="page-layout-content">
19
- <slot name="layout-content"></slot>
20
- </div>
21
-
22
- <NavFooter />
23
- </div>
24
- </template>
25
-
26
- <script setup lang="ts">
27
- useHead({
28
- bodyAttrs: {
29
- class: 'body-default',
30
- id: 'body',
31
- },
32
- // link: [
33
- // {
34
- // rel: 'preconnect',
35
- // href: 'https://fonts.googleapis.com',
36
- // },
37
- // {
38
- // rel: 'preconnect',
39
- // href: 'https://fonts.gstatic.com',
40
- // crossorigin: 'use-credentials',
41
- // },
42
- // {
43
- // href: 'https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap',
44
- // rel: 'stylesheet',
45
- // },
46
- // ],
47
- });
48
- </script>
49
-
50
- <style lang="css">
51
- .page-layout {
52
- display: grid;
53
- grid-template-rows: auto 1fr auto;
54
- }
55
-
56
- .page-layout-content {
57
- container: content / inline-size;
58
- }
59
-
60
- .flex-group {
61
- align-items: flex-start;
62
- display: flex;
63
- flex-wrap: wrap;
64
- gap: 24px;
65
- margin-bottom: 32px;
66
- }
67
-
68
- ul.flex-group {
69
- list-style-type: none;
70
- padding: 0;
71
- }
72
- </style>
@@ -1,27 +0,0 @@
1
- export default defineNuxtConfig({
2
- devtools: { enabled: true },
3
- extends: ['..'],
4
- app: {
5
- head: {
6
- htmlAttrs: {
7
- lang: 'en',
8
- },
9
- titleTemplate: '%s - Website name',
10
- meta: [{ charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }],
11
- },
12
- pageTransition: {
13
- name: 'page',
14
- mode: 'out-in',
15
- },
16
- layoutTransition: {
17
- name: 'layout',
18
- mode: 'out-in',
19
- },
20
- },
21
- components: [
22
- {
23
- path: './components',
24
- pathPrefix: false,
25
- },
26
- ],
27
- });
@@ -1,155 +0,0 @@
1
- <template>
2
- <div>
3
- <NuxtLayout name="default">
4
- <template #layout-content>
5
- <div>
6
- <h1>Example buttons</h1>
7
- <p>Primary submit</p>
8
-
9
- <p>Themes switcher</p>
10
- <ul class="flex-group">
11
- <li>
12
- <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('primary')" :is-pending="false" button-text="Primary" theme="primary" size="normal" />
13
- </li>
14
- <li>
15
- <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('secondary')" :is-pending="false" button-text="Secondary" theme="secondary" size="normal" />
16
- </li>
17
- <li>
18
- <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('tertiary')" :is-pending="false" button-text="Tertiary" theme="tertiary" size="normal" />
19
- </li>
20
- <li>
21
- <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('warning')" :is-pending="false" button-text="Warning" theme="warning" size="normal" />
22
- </li>
23
- <li>
24
- <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('success')" :is-pending="false" button-text="Success" theme="success" size="normal" />
25
- </li>
26
- <li>
27
- <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('error')" :is-pending="false" button-text="Error" theme="error" size="normal" />
28
- </li>
29
- <li>
30
- <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('ghost')" :is-pending="false" button-text="Ghost" theme="ghost" size="normal" />
31
- </li>
32
- </ul>
33
-
34
- <FormWrapper width="medium">
35
- <template #default>
36
- <form @submit.prevent="submitForm">
37
- <div class="flex-group">
38
- <InputButtonSubmit type="button" @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="x-small" />
39
-
40
- <InputButtonSubmit type="button" @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="small" />
41
-
42
- <InputButtonSubmit type="button" @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="normal" />
43
- <InputButtonSubmit type="button" @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="medium" />
44
- <InputButtonSubmit type="button" @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="large" />
45
- </div>
46
-
47
- <div class="flex-group">
48
- <InputButtonConfirm @click.stop.prevent="submitForm" :is-pending="false" button-text="Confirm" :theme size="x-small" />
49
- <InputButtonConfirm @click.stop.prevent="submitForm" :is-pending="false" button-text="Confirm" :theme size="small" />
50
- <InputButtonConfirm @click.stop.prevent="submitForm" :is-pending="false" button-text="Confirm" :theme size="normal" />
51
- <InputButtonConfirm @click.stop.prevent="submitForm" :is-pending="false" button-text="Confirm" :theme size="medium" />
52
- <InputButtonConfirm @click.stop.prevent="submitForm" :is-pending="false" button-text="Confirm" :theme size="large" />
53
- </div>
54
- <div class="flex-group">
55
- <InputButtonCore @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="x-small">
56
- <template #iconOnly>
57
- <Icon name="radix-icons:eye-none" class="icon" />
58
- </template>
59
- </InputButtonCore>
60
-
61
- <InputButtonCore @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="small">
62
- <template #iconOnly>
63
- <Icon name="radix-icons:eye-none" class="icon" />
64
- </template>
65
- </InputButtonCore>
66
-
67
- <InputButtonCore @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="normal">
68
- <template #iconOnly>
69
- <Icon name="radix-icons:eye-none" class="icon" />
70
- </template>
71
- </InputButtonCore>
72
-
73
- <InputButtonCore @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="medium">
74
- <template #iconOnly>
75
- <Icon name="radix-icons:eye-none" class="icon" />
76
- </template>
77
- </InputButtonCore>
78
-
79
- <InputButtonCore @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="large">
80
- <template #iconOnly>
81
- <Icon name="radix-icons:eye-none" class="icon" />
82
- </template>
83
- </InputButtonCore>
84
- </div>
85
- </form>
86
- </template>
87
- </FormWrapper>
88
- </div>
89
- </template>
90
- </NuxtLayout>
91
- </div>
92
- </template>
93
-
94
- <script setup lang="ts">
95
- import type { IFieldsInitialState, IOptionsConfig } from '@/types/types.forms';
96
-
97
- definePageMeta({
98
- layout: false,
99
- });
100
-
101
- useHead({
102
- title: 'Homepage',
103
- meta: [{ name: 'description', content: 'Homepage' }],
104
- bodyAttrs: {
105
- class: '',
106
- },
107
- });
108
-
109
- const theme = ref('primary');
110
-
111
- const swapTheme = (newTheme: string) => {
112
- theme.value = newTheme;
113
- };
114
-
115
- /*
116
- * Setup forms
117
- */
118
- const fieldsInitialState = ref<IFieldsInitialState>({
119
- emailAddress: '',
120
- username: '',
121
- password: '',
122
- });
123
-
124
- // Setup formData
125
- const { formData, initFormData, getErrorCount, updateErrorMessages, formIsValid, submitDisabled, useApiErrors } = useFormControl();
126
- await initFormData(fieldsInitialState);
127
-
128
- const submitForm = async () => {
129
- await getErrorCount(true);
130
-
131
- if (formIsValid.value) {
132
- formData.value.isPending = true;
133
- console.log('Form is good - post it!');
134
- // await useSleep(2000);
135
- // formData.value.isPending = false;
136
- } else {
137
- console.warn('Form has errors');
138
- }
139
- };
140
- </script>
141
-
142
- <style lang="css">
143
- .flex-group {
144
- align-items: flex-start;
145
- display: flex;
146
- flex-wrap: wrap;
147
- gap: 24px;
148
- margin-bottom: 32px;
149
- }
150
-
151
- ul.flex-group {
152
- list-style-type: none;
153
- padding: 0;
154
- }
155
- </style>
@@ -1,60 +0,0 @@
1
- <template>
2
- <div>
3
- <i a></i>
4
- <i b></i>
5
- <i c></i>
6
- </div>
7
- </template>
8
- <script setup lang="ts">
9
- definePageMeta({
10
- layout: false,
11
- });
12
-
13
- useHead({
14
- title: 'CSS Battle',
15
- meta: [{ name: 'description', content: 'CSS Battle' }],
16
- bodyAttrs: {
17
- class: '',
18
- },
19
- });
20
- </script>
21
-
22
- <style scoped lang="css">
23
- html * {
24
- margin: 0;
25
- padding: 0;
26
- }
27
- div {
28
- height: 300px;
29
- width: 400px;
30
- background: #0b2429;
31
- display: grid;
32
- grid-template-areas: stack;
33
- place-content: center;
34
-
35
- i {
36
- grid-area: stack;
37
- margin: auto;
38
- }
39
-
40
- i[a] {
41
- background: #998235;
42
- height: 300px;
43
- width: 90px;
44
- }
45
-
46
- i[b] {
47
- background: #0b2429;
48
- height: 130px;
49
- width: 100%;
50
- }
51
-
52
- i[c] {
53
- background: #f3ac3c;
54
- height: 90px;
55
- width: 130px;
56
- border-radius: 50px;
57
- outline: 10px solid #0b2429;
58
- }
59
- }
60
- </style>