srcdev-nuxt-forms 0.0.21 → 0.0.23
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/assets/styles/forms/index.css +2 -1
- package/assets/styles/forms/themes/_error.css +3 -0
- package/assets/styles/forms/themes/_primary.css +3 -0
- package/assets/styles/forms/themes/index.css +2 -0
- package/assets/styles/forms/variables/_theme.css +5 -21
- package/components/forms/input-text/InputTextCore.vue +23 -11
- package/components/forms/input-text/variants/InputTextMaterial.vue +92 -44
- package/components/forms/ui/FormField.vue +1 -1
- package/composables/useUpdateStyleClassPassthrough.ts +29 -0
- package/package.json +1 -1
|
@@ -1 +1,2 @@
|
|
|
1
|
-
@import
|
|
1
|
+
@import './variables/_theme.css';
|
|
2
|
+
@import './themes/index.css';
|
|
@@ -1,27 +1,11 @@
|
|
|
1
|
-
:
|
|
2
|
-
--
|
|
3
|
-
--
|
|
4
|
-
--form-text-color: #333333; /* Text color for form elements */
|
|
5
|
-
--form-placeholder-color: #888888; /* Placeholder text color */
|
|
6
|
-
--form-focus-border-color: #0076c6; /* Border color on focus */
|
|
7
|
-
--form-button-bg-color: #0076c6; /* Button background color */
|
|
8
|
-
--form-button-text-color: #ffffff; /* Button text color */
|
|
9
|
-
--form-error-color: #ce0606; /* Error message color */
|
|
10
|
-
--form-success-color: #008000; /* Success message color */
|
|
11
|
-
--form-warning-color: #ffcc00; /* Warning message color */
|
|
12
|
-
--form-info-color: #0076c6; /* Info message color */
|
|
13
|
-
--form-required-color: #ce0606; /* Required field color */
|
|
14
|
-
--form-disabled-color: #cccccc; /* Disabled field color */
|
|
15
|
-
--form-disabled-bg-color: #f9f9f9; /* Disabled field background color */
|
|
16
|
-
--form-disabled-border-color: #cccccc; /* Disabled field border color */
|
|
17
|
-
--form-disabled-text-color: #999999; /* Disabled field text color */
|
|
18
|
-
|
|
19
|
-
--input-border: #121212;
|
|
1
|
+
:where(html) {
|
|
2
|
+
--font-size: 16px;
|
|
3
|
+
--line-height: calc((var(--font-size) * 2) - 10px);
|
|
20
4
|
|
|
21
5
|
--input-border-radius: 4px;
|
|
22
|
-
|
|
23
6
|
--input-border-width-thin: 1px;
|
|
24
|
-
--input-border-width-
|
|
7
|
+
--input-border-width-default: 2px;
|
|
8
|
+
--input-border-width-thick: 3px;
|
|
25
9
|
|
|
26
10
|
--font-family: futura-pt, Seravek, 'Gill Sans Nova', Ubuntu, Calibri,
|
|
27
11
|
'DejaVu Sans', source-sans-pro, sans-serif;
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
'input-text',
|
|
12
12
|
'text-normal',
|
|
13
13
|
styleClassPassthrough,
|
|
14
|
+
{ active: isFocused },
|
|
15
|
+
{ dirty: isDirty },
|
|
14
16
|
{ error: fieldHasError() },
|
|
15
17
|
]"
|
|
16
18
|
v-model="modelValue.data[name]"
|
|
@@ -67,10 +69,12 @@ const props = defineProps({
|
|
|
67
69
|
|
|
68
70
|
const modelValue = defineModel() as Ref<IFormData>;
|
|
69
71
|
const isFocused = defineModel('isFocused') as Ref<boolean>;
|
|
72
|
+
const isDirty = defineModel('isDirty') as Ref<boolean>;
|
|
70
73
|
|
|
71
74
|
const name = computed(() => {
|
|
72
75
|
return props.name !== null ? props.name : props.id;
|
|
73
76
|
});
|
|
77
|
+
|
|
74
78
|
const validatorLocale = toRef(useRuntimeConfig().public.validatorLocale);
|
|
75
79
|
|
|
76
80
|
const componentValidation =
|
|
@@ -99,6 +103,14 @@ watchEffect(() => {
|
|
|
99
103
|
'InputTextCore >> Form value changed to: ',
|
|
100
104
|
modelValue.value.data[name.value]
|
|
101
105
|
);
|
|
106
|
+
|
|
107
|
+
isDirty.value = modelValue.value.data[name.value] !== '';
|
|
108
|
+
|
|
109
|
+
modelValue.value!.validityState[name.value] =
|
|
110
|
+
inputField.value?.validity.valid ?? false;
|
|
111
|
+
if (hasCustomError()) {
|
|
112
|
+
removeCustomError(inputField.value?.validity.valid);
|
|
113
|
+
}
|
|
102
114
|
});
|
|
103
115
|
|
|
104
116
|
const isValid = () => {
|
|
@@ -109,17 +121,17 @@ const isValid = () => {
|
|
|
109
121
|
};
|
|
110
122
|
|
|
111
123
|
// Keep an eye on this for performance issue
|
|
112
|
-
watch(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
);
|
|
124
|
+
// watch(
|
|
125
|
+
// () => modelValue.value.data[name.value],
|
|
126
|
+
// () => {
|
|
127
|
+
// modelValue.value!.validityState[name.value] =
|
|
128
|
+
// inputField.value?.validity.valid ?? false;
|
|
129
|
+
// if (hasCustomError()) {
|
|
130
|
+
// removeCustomError(inputField.value?.validity.valid);
|
|
131
|
+
// }
|
|
132
|
+
// },
|
|
133
|
+
// { deep: true }
|
|
134
|
+
// );
|
|
123
135
|
|
|
124
136
|
onMounted(() => {
|
|
125
137
|
isValid();
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="input-text-material">
|
|
3
|
-
<label
|
|
4
|
-
|
|
2
|
+
<div class="input-text-material" :class="[{ error: fieldHasError }]">
|
|
3
|
+
<label
|
|
4
|
+
class="label"
|
|
5
|
+
:class="[{ active: isFocused }, { dirty: isDirty }]"
|
|
6
|
+
:for="id"
|
|
7
|
+
>{{ labelText }}</label
|
|
5
8
|
>
|
|
6
|
-
<div class="input-
|
|
9
|
+
<div class="input-text-container">
|
|
7
10
|
<InputTextCore
|
|
8
11
|
:id
|
|
9
12
|
:name
|
|
@@ -12,8 +15,9 @@
|
|
|
12
15
|
:required
|
|
13
16
|
v-model="modelValue"
|
|
14
17
|
v-model:isFocused="isFocused"
|
|
18
|
+
v-model:isDirty="isDirty"
|
|
15
19
|
:c12
|
|
16
|
-
:style-class-passthrough="
|
|
20
|
+
:style-class-passthrough="styleClassPassthroughRef"
|
|
17
21
|
/>
|
|
18
22
|
</div>
|
|
19
23
|
</div>
|
|
@@ -58,85 +62,129 @@ const props = defineProps({
|
|
|
58
62
|
type: Object as PropType<InpuTextC12>,
|
|
59
63
|
required: true,
|
|
60
64
|
},
|
|
65
|
+
styleClassPassthrough: {
|
|
66
|
+
type: String,
|
|
67
|
+
default: '',
|
|
68
|
+
},
|
|
61
69
|
});
|
|
62
70
|
|
|
63
71
|
const name = computed(() => {
|
|
64
72
|
return props.name !== null ? props.name : props.id;
|
|
65
73
|
});
|
|
66
74
|
|
|
67
|
-
const
|
|
68
|
-
return
|
|
75
|
+
const labelText = computed(() => {
|
|
76
|
+
return fieldHasError.value ? errorMessage.value : props.c12.label;
|
|
69
77
|
});
|
|
70
78
|
|
|
79
|
+
const { styleClassPassthroughRef, updateClasses } =
|
|
80
|
+
useUpdateStyleClassPassthrough(props.styleClassPassthrough);
|
|
81
|
+
|
|
71
82
|
const modelValue = defineModel() as Ref<IFormData>;
|
|
72
83
|
const isFocused = ref(false);
|
|
84
|
+
const isDirty = ref(false);
|
|
85
|
+
|
|
86
|
+
const { errorMessage, setDefaultError, fieldHasError } = useErrorMessage(
|
|
87
|
+
name.value,
|
|
88
|
+
modelValue
|
|
89
|
+
);
|
|
90
|
+
setDefaultError(props.c12.errorMessage);
|
|
73
91
|
</script>
|
|
74
92
|
|
|
75
93
|
<style lang="css">
|
|
76
94
|
.input-text-material {
|
|
95
|
+
input {
|
|
96
|
+
background-color: transparent;
|
|
97
|
+
line-height: var(--line-height);
|
|
98
|
+
|
|
99
|
+
&:focus {
|
|
100
|
+
outline: none;
|
|
101
|
+
box-shadow: none;
|
|
102
|
+
border: none;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
label {
|
|
107
|
+
margin: initial;
|
|
108
|
+
line-height: var(--line-height);
|
|
109
|
+
padding: initial;
|
|
110
|
+
}
|
|
111
|
+
|
|
77
112
|
--_gutter: 12px;
|
|
113
|
+
--_form-theme: var(--theme-primary);
|
|
114
|
+
--_border-width: var(--input-border-width-default);
|
|
78
115
|
|
|
79
116
|
display: grid;
|
|
80
|
-
grid-template-columns: 1fr;
|
|
81
|
-
/* grid-template-rows: var(--_gutter) 1fr var(--_gutter); */
|
|
82
|
-
grid-template-rows: var(--_gutter) 1fr var(--_gutter);
|
|
83
|
-
gap: calc(var(--_gutter) / 2);
|
|
84
|
-
|
|
85
|
-
border: 1px solid red;
|
|
86
117
|
border-radius: 2px;
|
|
118
|
+
outline: var(--_border-width) solid var(--_form-theme);
|
|
87
119
|
|
|
88
120
|
margin-bottom: 20px;
|
|
89
|
-
|
|
121
|
+
overflow: hidden;
|
|
122
|
+
/* transition: all linear 0.2s; */
|
|
123
|
+
|
|
124
|
+
&:focus-within {
|
|
125
|
+
outline: calc(var(--_border-width) * 2) solid var(--_form-theme);
|
|
126
|
+
background-color: hsl(from var(--_form-theme) h s 95%);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&.error {
|
|
130
|
+
outline: calc(var(--_border-width) * 2) solid var(--theme-error);
|
|
131
|
+
background-color: hsl(from var(--theme-error) h s 95%);
|
|
132
|
+
|
|
133
|
+
/* .label {
|
|
134
|
+
color: var(--theme-error);
|
|
135
|
+
} */
|
|
136
|
+
}
|
|
90
137
|
|
|
91
138
|
.label {
|
|
92
|
-
grid-row:
|
|
139
|
+
grid-row: 1;
|
|
93
140
|
grid-column: 1;
|
|
94
141
|
|
|
95
142
|
font-family: var(--font-family);
|
|
96
|
-
font-size:
|
|
143
|
+
font-size: 20px;
|
|
97
144
|
font-weight: 700;
|
|
98
145
|
padding: var(--_gutter);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
z-index: 2;
|
|
102
|
-
|
|
146
|
+
transform: translateY(12px);
|
|
103
147
|
transition: all linear 0.2s;
|
|
148
|
+
background-color: transparent;
|
|
104
149
|
|
|
105
|
-
&.active
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
transform: translateY(-10px);
|
|
150
|
+
&.active,
|
|
151
|
+
&.dirty {
|
|
152
|
+
font-size: 14px;
|
|
153
|
+
transform: translateY(0);
|
|
110
154
|
}
|
|
111
155
|
}
|
|
112
156
|
|
|
113
|
-
.input-
|
|
114
|
-
|
|
157
|
+
.input-text-container {
|
|
158
|
+
display: grid;
|
|
159
|
+
grid-row: 1;
|
|
115
160
|
grid-column: 1;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
transform: translateY(12px);
|
|
161
|
+
margin-top: 2rem;
|
|
162
|
+
background-color: transparent;
|
|
119
163
|
|
|
120
164
|
.input-text {
|
|
121
165
|
font-family: var(--font-family);
|
|
122
166
|
border: 0px solid green;
|
|
123
|
-
padding: var(--_gutter);
|
|
124
|
-
font-size:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
167
|
+
padding: calc(var(--_gutter) / 2) var(--_gutter);
|
|
168
|
+
font-size: var(--font-size);
|
|
169
|
+
margin: 0;
|
|
170
|
+
opacity: 0;
|
|
171
|
+
transition: opacity linear 0.2s;
|
|
172
|
+
|
|
173
|
+
&.active,
|
|
174
|
+
&.dirty {
|
|
175
|
+
opacity: 1;
|
|
176
|
+
}
|
|
177
|
+
/*
|
|
178
|
+
&::placeholder,
|
|
179
|
+
&:-ms-input-placeholder,
|
|
180
|
+
&::-moz-placeholder, */
|
|
129
181
|
&::-webkit-input-placeholder {
|
|
130
182
|
font-family: var(--font-family);
|
|
131
|
-
color: var(--gray-5);
|
|
132
|
-
|
|
183
|
+
/* color: var(--gray-5); */
|
|
184
|
+
color: hsl(from var(--_form-theme) h s 50%);
|
|
185
|
+
font-size: var(--font-size);
|
|
133
186
|
font-style: italic;
|
|
134
|
-
font-weight:
|
|
135
|
-
opacity: 0;
|
|
136
|
-
|
|
137
|
-
&.is-active {
|
|
138
|
-
opacity: 1;
|
|
139
|
-
}
|
|
187
|
+
font-weight: 500;
|
|
140
188
|
}
|
|
141
189
|
}
|
|
142
190
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const useUpdateStyleClassPassthrough = (classes: string) => {
|
|
2
|
+
const styleClassPassthroughRef = ref(classes);
|
|
3
|
+
|
|
4
|
+
function updateClasses(add: boolean, cssClass: string) {
|
|
5
|
+
let classesArray = classes.split(' ');
|
|
6
|
+
|
|
7
|
+
if (add && !classesArray.includes(cssClass)) {
|
|
8
|
+
classesArray.push(cssClass);
|
|
9
|
+
} else if (!add && classesArray.includes(cssClass)) {
|
|
10
|
+
classesArray = classesArray.filter((className) => className !== cssClass);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// if (classesArray.includes(cssClass)) {
|
|
14
|
+
// // Remove the value if it's already in the array
|
|
15
|
+
// classesArray = classesArray.filter(className => className !== cssClass);
|
|
16
|
+
// } else {
|
|
17
|
+
// // Add the value if it's not in the array
|
|
18
|
+
// classesArray.push(cssClass);
|
|
19
|
+
// }
|
|
20
|
+
|
|
21
|
+
// Join the array back into a string and assign it back
|
|
22
|
+
styleClassPassthroughRef.value = classesArray.join(' ');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
styleClassPassthroughRef,
|
|
27
|
+
updateClasses,
|
|
28
|
+
};
|
|
29
|
+
};
|