srcdev-nuxt-forms 0.0.18 → 0.0.20

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/.prettierrc CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "semi": false,
3
- "singleQuote": true
4
- }
2
+ "semi": true,
3
+ "singleQuote": true
4
+ }
@@ -0,0 +1,15 @@
1
+ :where(html) {
2
+ --blue-0: #e7f5ff;
3
+ --blue-1: #d0ebff;
4
+ --blue-2: #a5d8ff;
5
+ --blue-3: #74c0fc;
6
+ --blue-4: #4dabf7;
7
+ --blue-5: #339af0;
8
+ --blue-6: #228be6;
9
+ --blue-7: #1c7ed6;
10
+ --blue-8: #1971c2;
11
+ --blue-9: #1864ab;
12
+ --blue-10: #145591;
13
+ --blue-11: #114678;
14
+ --blue-12: #0d375e;
15
+ }
@@ -0,0 +1,15 @@
1
+ :where(html) {
2
+ --green-0: #ebfbee;
3
+ --green-1: #d3f9d8;
4
+ --green-2: #b2f2bb;
5
+ --green-3: #8ce99a;
6
+ --green-4: #69db7c;
7
+ --green-5: #51cf66;
8
+ --green-6: #40c057;
9
+ --green-7: #37b24d;
10
+ --green-8: #2f9e44;
11
+ --green-9: #2b8a3e;
12
+ --green-10: #237032;
13
+ --green-11: #1b5727;
14
+ --green-12: #133d1b;
15
+ }
@@ -0,0 +1,15 @@
1
+ :where(html) {
2
+ --gray-0: #f8f9fa;
3
+ --gray-1: #f1f3f5;
4
+ --gray-2: #e9ecef;
5
+ --gray-3: #dee2e6;
6
+ --gray-4: #ced4da;
7
+ --gray-5: #adb5bd;
8
+ --gray-6: #868e96;
9
+ --gray-7: #495057;
10
+ --gray-8: #343a40;
11
+ --gray-9: #212529;
12
+ --gray-10: #16191d;
13
+ --gray-11: #0d0f12;
14
+ --gray-12: #030507;
15
+ }
@@ -0,0 +1,15 @@
1
+ :where(html) {
2
+ --orange-0: #fff4e6;
3
+ --orange-1: #ffe8cc;
4
+ --orange-2: #ffd8a8;
5
+ --orange-3: #ffc078;
6
+ --orange-4: #ffa94d;
7
+ --orange-5: #ff922b;
8
+ --orange-6: #fd7e14;
9
+ --orange-7: #f76707;
10
+ --orange-8: #e8590c;
11
+ --orange-9: #d9480f;
12
+ --orange-10: #bf400d;
13
+ --orange-11: #99330b;
14
+ --orange-12: #802b09;
15
+ }
@@ -0,0 +1,15 @@
1
+ :where(html) {
2
+ --red-0: #fff5f5;
3
+ --red-1: #ffe3e3;
4
+ --red-2: #ffc9c9;
5
+ --red-3: #ffa8a8;
6
+ --red-4: #ff8787;
7
+ --red-5: #ff6b6b;
8
+ --red-6: #fa5252;
9
+ --red-7: #f03e3e;
10
+ --red-8: #e03131;
11
+ --red-9: #c92a2a;
12
+ --red-10: #b02525;
13
+ --red-11: #962020;
14
+ --red-12: #7d1a1a;
15
+ }
@@ -0,0 +1,15 @@
1
+ :where(html) {
2
+ --yellow-0: #fff9db;
3
+ --yellow-1: #fff3bf;
4
+ --yellow-2: #ffec99;
5
+ --yellow-3: #ffe066;
6
+ --yellow-4: #ffd43b;
7
+ --yellow-5: #fcc419;
8
+ --yellow-6: #fab005;
9
+ --yellow-7: #f59f00;
10
+ --yellow-8: #f08c00;
11
+ --yellow-9: #e67700;
12
+ --yellow-10: #b35c00;
13
+ --yellow-11: #804200;
14
+ --yellow-12: #663500;
15
+ }
@@ -0,0 +1,6 @@
1
+ @import './_red.css';
2
+ @import './_green.css';
3
+ @import './_blue.css';
4
+ @import './_orange.css';
5
+ @import './_gray.css';
6
+ @import './_yellow.css';
@@ -1 +1 @@
1
- @import "./_colours.css";
1
+ @import './_colours/index.css';
@@ -0,0 +1,44 @@
1
+ {
2
+ "username": {
3
+ "pattern": "^[a-zA-Z0-9_\\-'.]{8,20}$",
4
+ "minlength": 8,
5
+ "maxlength": 20,
6
+ "hint": "Uppercase letters and numbers, with ,.- and space"
7
+ },
8
+ "usernameWeak": {
9
+ "pattern": "^[a-zA-Z0-9]{6,20}$",
10
+ "minlength": 6,
11
+ "maxlength": 20,
12
+ "hint": "Mixed case letters"
13
+ },
14
+ "password": {
15
+ "pattern": "^(?=.*\\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\\w\\d\\s:])([^\\s]){8,16}$",
16
+ "minlength": 8,
17
+ "maxlength": 20,
18
+ "hint": "!Pa55word"
19
+ },
20
+ "passwordWeak": {
21
+ "pattern": "^[a-zA-Z0-9]{6,20}$",
22
+ "minlength": 6,
23
+ "maxlength": 20,
24
+ "hint": "password"
25
+ },
26
+ "emailaddress": {
27
+ "pattern": "^(?!@)((?!([\\.]))([\\w\\.\\-\\+']{1,}))?((@)([\\w\\-]{2,})+((\\.)([\\w]{2,}))+)$",
28
+ "minlength": 7,
29
+ "maxlength": 255,
30
+ "hint": "you@your-email.com"
31
+ },
32
+ "url": {
33
+ "pattern": "^((http|https)://){1}([\\w\\-]{2,}(\\.))+([\\w]{2,})((/))*((/)[\\w\\-]{1,})*((/)[\\w]{1,}(/))*(/)*$",
34
+ "minlength": 13,
35
+ "maxlength": 255,
36
+ "hint": "https://your-website.com"
37
+ },
38
+ "telephone": {
39
+ "pattern": "^(0|\\+44|\\+353)([\\d\\- ]{10})$",
40
+ "minlength": 11,
41
+ "maxlength": 14,
42
+ "hint": "+441632123123"
43
+ }
44
+ }
@@ -0,0 +1,6 @@
1
+ import en from './en.json'
2
+
3
+ export const validationConfig: any = {
4
+ 'en-GB': en,
5
+ }
6
+ export default validationConfig
@@ -1,22 +1,124 @@
1
1
  <template>
2
- <div>
3
- <input type="text" class="input-text" v-model="formValue" />
4
- </div>
2
+ <input
3
+ :type
4
+ :placeholder="c12.placeholder"
5
+ :id
6
+ :name
7
+ :pattern="componentValidation.pattern"
8
+ :maxlength="componentValidation.maxlength"
9
+ :required
10
+ :class="['input-text', 'text-normal', { error: fieldHasError() }]"
11
+ v-model="modelValue.data[name]"
12
+ ref="inputField"
13
+ @focusin="isFocused = true"
14
+ @focusout="isFocused = false"
15
+ />
5
16
  </template>
17
+
6
18
  <script setup lang="ts">
7
- console.log('InputText component loaded')
19
+ import type { InpuTextC12, IFormData } from '@/types/types.forms';
20
+ import { validationConfig } from '@/components/forms/c12/validation-patterns';
21
+
22
+ const props = defineProps({
23
+ type: {
24
+ // type: String as PropType<"text" | "password" | "tel" | "number" | "email" | "url">, // This breaks props setup in unit tests
25
+ type: String,
26
+ validator(value: string) {
27
+ return ['text', 'password', 'tel', 'number', 'email', 'url'].includes(
28
+ value
29
+ );
30
+ },
31
+ },
32
+ id: {
33
+ // type: String as PropType<string>,
34
+ type: String,
35
+ required: true,
36
+ },
37
+ name: {
38
+ type: String,
39
+ default: null,
40
+ },
41
+ validation: {
42
+ type: String,
43
+ default: '',
44
+ },
45
+ required: {
46
+ type: Boolean,
47
+ value: false,
48
+ },
49
+ isPending: {
50
+ type: Boolean,
51
+ value: false,
52
+ },
53
+ c12: {
54
+ type: Object as PropType<InpuTextC12>,
55
+ required: true,
56
+ },
57
+ });
58
+
59
+ const modelValue = defineModel() as Ref<IFormData>;
60
+ const isFocused = defineModel('isFocused') as Ref<boolean>;
61
+
62
+ const name = computed(() => {
63
+ return props.name !== null ? props.name : props.id;
64
+ });
65
+ const validatorLocale = toRef(useRuntimeConfig().public.validatorLocale);
66
+
67
+ const componentValidation =
68
+ validationConfig[validatorLocale.value][props.validation];
69
+ const inputField = ref<HTMLInputElement | null>(null);
8
70
 
9
- const formValue = ref('')
71
+ const { hasCustomError, removeCustomError } = useErrorMessage(
72
+ name.value,
73
+ modelValue
74
+ );
75
+
76
+ const fieldHasError = () => {
77
+ const hasApiErrorMessage = hasCustomError();
78
+ const inputBad = !inputField.value?.validity.valid;
79
+
80
+ if (modelValue.value.isPending) {
81
+ modelValue.value!.validityState[name.value] =
82
+ inputField.value?.validity.valid ?? false;
83
+ return hasApiErrorMessage ? hasApiErrorMessage : inputBad;
84
+ }
85
+ return false;
86
+ };
10
87
 
11
88
  watchEffect(() => {
12
- console.log('Form value changed to: ', formValue.value)
13
- })
89
+ console.log(
90
+ 'InputTextCore >> Form value changed to: ',
91
+ modelValue.value.data[name.value]
92
+ );
93
+ });
94
+
95
+ const isValid = () => {
96
+ setTimeout(() => {
97
+ modelValue.value!.validityState[name.value] =
98
+ inputField.value?.validity.valid ?? false;
99
+ }, 0);
100
+ };
101
+
102
+ // Keep an eye on this for performance issue
103
+ watch(
104
+ () => modelValue.value.data[name.value],
105
+ () => {
106
+ modelValue.value!.validityState[name.value] =
107
+ inputField.value?.validity.valid ?? false;
108
+ if (hasCustomError()) {
109
+ removeCustomError(inputField.value?.validity.valid);
110
+ }
111
+ },
112
+ { deep: true }
113
+ );
114
+
115
+ onMounted(() => {
116
+ isValid();
117
+ });
14
118
  </script>
15
119
 
16
120
  <style lang="css">
17
- .input-text {
18
- border: var(--input-border-width-thin) solid var(--input-border);
19
- border-radius: var(--input-border-radius);
20
- padding: 10px;
21
- }
121
+ /* .input-text {
122
+ transition: all linear 200ms;
123
+ } */
22
124
  </style>
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <div class="input-text-material">
3
+ <label :for="id">{{ c12.label }}</label>
4
+ <InputTextCore
5
+ :id
6
+ :name
7
+ :type
8
+ :validation
9
+ :required
10
+ v-model="modelValue"
11
+ v-model:isFocused="isFocused"
12
+ :c12
13
+ />
14
+ </div>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import type { InpuTextC12, IFormData } from '@/types/types.forms';
19
+ import { validationConfig } from '@/components/forms/c12/validation-patterns';
20
+
21
+ const props = defineProps({
22
+ type: {
23
+ // type: String as PropType<"text" | "password" | "tel" | "number" | "email" | "url">, // This breaks props setup in unit tests
24
+ type: String,
25
+ validator(value: string) {
26
+ return ['text', 'password', 'tel', 'number', 'email', 'url'].includes(
27
+ value
28
+ );
29
+ },
30
+ },
31
+ id: {
32
+ // type: String as PropType<string>,
33
+ type: String,
34
+ required: true,
35
+ },
36
+ name: {
37
+ type: String,
38
+ default: null,
39
+ },
40
+ validation: {
41
+ type: String,
42
+ default: '',
43
+ },
44
+ required: {
45
+ type: Boolean,
46
+ value: false,
47
+ },
48
+ isPending: {
49
+ type: Boolean,
50
+ value: false,
51
+ },
52
+ c12: {
53
+ type: Object as PropType<InpuTextC12>,
54
+ required: true,
55
+ },
56
+ });
57
+
58
+ const name = computed(() => {
59
+ return props.name !== null ? props.name : props.id;
60
+ });
61
+
62
+ const modelValue = defineModel() as Ref<IFormData>;
63
+ const isFocused = ref(false);
64
+ </script>
65
+
66
+ <style lang="css">
67
+ .input-text-material {
68
+ display: flex;
69
+ flex-direction: column;
70
+
71
+ border: 1px solid black;
72
+ border-radius: 2px;
73
+
74
+ margin-bottom: 20px;
75
+
76
+ .input-text {
77
+ border: 1px solid red;
78
+ margin: 5px;
79
+
80
+ /* &::placeholder,
81
+ &:-ms-input-placeholder,
82
+ &::-moz-placeholder, */
83
+ &::-webkit-input-placeholder {
84
+ color: rgb(128, 0, 117);
85
+ font-size: 30px;
86
+ }
87
+ }
88
+ }
89
+ </style>
package/nuxt.config.ts CHANGED
@@ -5,6 +5,12 @@ const { resolve } = createResolver(import.meta.url)
5
5
  export default defineNuxtConfig({
6
6
  devtools: { enabled: true },
7
7
  css: [resolve('./assets/styles/main.css')],
8
+ runtimeConfig: {
9
+ public: {
10
+ validatorLocale: 'en-GB',
11
+ },
12
+ },
13
+
8
14
  components: [
9
15
  {
10
16
  path: './components',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-forms",
3
3
  "type": "module",
4
- "version": "0.0.18",
4
+ "version": "0.0.20",
5
5
  "main": "./nuxt.config.ts",
6
6
  "scripts": {
7
7
  "reinstall": "rm -rf node_modules && npm install",
package/pages/index.vue CHANGED
@@ -7,27 +7,52 @@
7
7
 
8
8
  <FormWrapper width="medium">
9
9
  <template #default>
10
- <form>
10
+ <form @submit.prevent="isPending">
11
11
  <p>Form content</p>
12
12
  <FormField width="wide" :has-gutter="true">
13
13
  <template #default>
14
14
  <p>Input text</p>
15
- <InputTextCore />
15
+ <InputTextMaterial
16
+ id="username"
17
+ name="username"
18
+ type="text"
19
+ validation="username"
20
+ :required="true"
21
+ :c12="{
22
+ label: 'Choose Username',
23
+ placeholder: 'eg. YourUserName',
24
+ errorMessage: 'Please enter a valid username',
25
+ }"
26
+ v-model="formData"
27
+ />
16
28
  </template>
17
29
  </FormField>
30
+ <input
31
+ type="submit"
32
+ @click.prevent="isPending"
33
+ value="Submit"
34
+ />
18
35
  </form>
19
36
  </template>
20
37
  </FormWrapper>
21
38
  </div>
39
+ <ClientOnly>
40
+ <p>Client only content</p>
41
+ <pre>
42
+ {{ formData }}
43
+ </pre>
44
+ </ClientOnly>
22
45
  </template>
23
46
  </NuxtLayout>
24
47
  </div>
25
48
  </template>
26
49
 
27
50
  <script setup lang="ts">
51
+ import type { IFieldsInitialState, IOptionsConfig } from '@/types/types.forms';
52
+
28
53
  definePageMeta({
29
54
  layout: false,
30
- })
55
+ });
31
56
 
32
57
  useHead({
33
58
  title: 'Homepage',
@@ -35,7 +60,38 @@ useHead({
35
60
  bodyAttrs: {
36
61
  class: '',
37
62
  },
38
- })
63
+ });
64
+
65
+ /*
66
+ * Setup forms
67
+ */
68
+ const fieldsInitialState = ref<IFieldsInitialState>({
69
+ username: '',
70
+ });
71
+
72
+ // Setup formData
73
+ const {
74
+ formData,
75
+ initFormData,
76
+ getErrorCount,
77
+ updateCustomErrors,
78
+ resetForm,
79
+ formIsValid,
80
+ showErrors,
81
+ } = useFormControl(fieldsInitialState);
82
+
83
+ await initFormData();
84
+
85
+ const isPending = async () => {
86
+ formData.value.isPending = true;
87
+ await getErrorCount();
88
+
89
+ if (formIsValid.value) {
90
+ console.log('Form is good - post it!');
91
+ } else {
92
+ console.warn('Form has errors');
93
+ }
94
+ };
39
95
  </script>
40
96
 
41
97
  <style lang="css">
@@ -17,7 +17,14 @@ export interface IOptionsValueArr {
17
17
  }
18
18
 
19
19
  export interface IFieldsInitialState {
20
- [key: string]: null | string | boolean | number | URL | object | IOptionsValueArr[];
20
+ [key: string]:
21
+ | null
22
+ | string
23
+ | boolean
24
+ | number
25
+ | URL
26
+ | object
27
+ | IOptionsValueArr[];
21
28
  }
22
29
 
23
30
  export interface IValidityState {
@@ -59,6 +66,12 @@ export interface ICustomErrorMessage {
59
66
  message: string;
60
67
  }
61
68
 
69
+ export interface InpuTextC12 {
70
+ label: string;
71
+ placeholder: string;
72
+ errorMessage: string;
73
+ }
74
+
62
75
  export interface ICustomErrorMessagesArr {
63
76
  [x: string]: ICustomErrorMessage;
64
77
  }