PluginProbe
The Innovative Form Builder – IvyForms / 0.1.2
The Innovative Form Builder – IvyForms v0.1.2
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / views / _components / input / IvyTextInput.vue

IvyTextInput.vue in The Innovative Form Builder – IvyForms 0.1.2, at frontend/src/views/_components/input/IvyTextInput.vue

330 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <template>
2 <ElInput
3 ref="elInputRef"
4 v-bind="{ ...$attrs, ...$props }"
5 v-model="localModelValue"
6 :class="[
7 type === 'textarea' ? 'ivyforms-textarea' : 'ivyforms-input',
8 { 'is-filled': props.filled, 'is-secondary': props.secondary },
9 ]"
10 :type="localInputType"
11 @focus="isFocused = true"
12 @blur="isFocused = false"
13 >
14 <template v-if="!!slots['prefix'] || iconStart" #prefix>
15 <slot name="prefix">
16 <IvyIcon
17 v-if="iconStart"
18 :name="iconStart"
19 :category="iconStartCategory"
20 :type="iconStartType"
21 class="ivyforms-input__icon-start"
22 size="l"
23 color="var(--map-base-dusk-symbol-2)"
24 />
25 </slot>
26 </template>
27 <template v-if="!!slots['suffix'] || iconEnd || props.clearable" #suffix>
28 <slot name="suffix">
29 <IvyIcon
30 v-if="props.iconEnd"
31 :name="props.iconEnd"
32 :category="props.iconEndCategory"
33 :type="props.iconEndType"
34 class="ivyforms-input__icon-end"
35 size="l"
36 color="var(--map-base-dusk-symbol-2)"
37 />
38 <IvyIcon
39 v-if="props.clearable && localModelValue"
40 name="close-circle"
41 class="ivyforms-input__clear"
42 size="l"
43 color="var(--map-base-dusk-symbol-2)"
44 type="fill-duo"
45 @click.stop="clearInput"
46 />
47 </slot>
48 </template>
49 </ElInput>
50 </template>
51
52 <script setup lang="ts">
53 import { ref, watch, useSlots } from 'vue'
54 import type { IconCategory } from '@/types/icons/icon-category'
55 import type { IconType } from '@/types/icons/icon-type'
56
57 interface Props {
58 type?: 'text' | 'textarea' | 'number' | 'select' | 'multiple-select' | 'phone' | 'email'
59 placeholder?: string
60 iconStart?: string
61 iconEnd?: string
62 iconStartCategory?: IconCategory
63 iconEndCategory?: IconCategory
64 avatar?: string
65 iconStartType?: IconType
66 iconEndType?: IconType
67 filled?: boolean
68 secondary?: boolean
69 prefix?: string
70 suffix?: string
71 clearable?: boolean
72 modelValue: string | number | null | undefined
73 }
74
75 const props = withDefaults(defineProps<Props>(), {
76 type: 'text',
77 placeholder: '',
78 iconStart: '',
79 iconEnd: '',
80 iconStartCategory: 'global',
81 iconEndCategory: 'global',
82 iconStartType: 'outline',
83 iconEndType: 'outline',
84 avatar: '',
85 prefix: '',
86 suffix: '',
87 })
88
89 const emit = defineEmits(['update:modelValue'])
90
91 const localModelValue = ref(props.modelValue)
92 const localInputType = ref(props.type)
93
94 const slots = useSlots()
95
96 const elInputRef = ref()
97 const isFocused = ref(false)
98
99 // Sync localModelValue with props.modelValue
100 watch(
101 () => props.modelValue,
102 (newValue) => {
103 localModelValue.value = newValue
104 },
105 )
106
107 // Emit update:modelValue when localModelValue changes
108 watch(localModelValue, (newValue) => {
109 emit('update:modelValue', newValue)
110 })
111
112 const clearInput = () => {
113 localModelValue.value = ''
114 }
115
116 const focus = () => {
117 elInputRef.value?.focus()
118 }
119
120 defineExpose({
121 focus,
122 })
123 </script>
124
125 <style lang="scss">
126 // Input
127 .ivyforms-input {
128 &.el-input {
129 height: 40px;
130
131 .el-input__wrapper {
132 display: flex;
133 align-items: center;
134 gap: var(--Spacing-sm, 8px);
135 padding: var(--Spacing-sm, 4px) var(--Spacing-lg, 12px);
136 align-self: stretch;
137 border-radius: var(--Radius-radius-md, 8px);
138 border: 1px solid var(--map-base-dusk-stroke--2);
139 background: var(--map-base-dusk-o05);
140 box-shadow: none;
141
142 .el-input__prefix-inner > :last-child {
143 margin-right: 0;
144 }
145
146 // Hover
147 &:hover,
148 &.is-hover {
149 border: 1px solid var(--map-base-dusk-stroke-0);
150 background: var(--map-base-dusk-o05);
151 }
152
153 // Inner input
154 input {
155 padding: 0;
156 font-size: 16px;
157 font-style: normal;
158 font-weight: 400;
159 line-height: 22px;
160 color: var(--map-base-text-0);
161 border: none;
162 box-shadow: none;
163 background-color: transparent;
164 display: flex;
165 align-items: center;
166 gap: 4px;
167 flex: 1 0 0;
168 }
169
170 // Active & Focus
171 &:active,
172 &:focus,
173 &.is-focus,
174 &.is-active {
175 border: 1px solid var(--map-base-brand-stroke-0);
176 background: var(--map-base-dusk-o05);
177 box-shadow: none;
178 }
179
180 // Clear Icon
181 .ivyforms-input__clear {
182 cursor: pointer;
183 width: 20px;
184 height: 20px;
185 transform: scale(1);
186 margin: 0;
187 }
188
189 .el-input__clear {
190 display: none !important;
191
192 &::before {
193 display: none !important;
194 }
195 }
196 }
197
198 // Secondary
199 &.is-secondary {
200 .el-input__wrapper {
201 // Active & Focus
202 &:active,
203 &:focus,
204 &.is-focus,
205 &.is-active {
206 border: 1px solid var(--map-base-purple-stroke-0);
207 }
208 }
209 }
210 }
211 }
212
213 // Form Item
214 .el-form-item {
215 &.ivyforms-form-item-input {
216 // Secondary
217 &.is-secondary {
218 .ivyforms-input {
219 &.el-input {
220 .el-input__wrapper {
221 // Active & Focus
222 &:active,
223 &:focus,
224 &.is-focus,
225 &.is-active {
226 border: 1px solid var(--map-base-purple-stroke-0);
227 }
228 }
229 }
230 }
231 }
232 }
233
234 // Error and warning states
235 &.is-error {
236 .ivyforms-input {
237 &.el-input {
238 .el-input__wrapper {
239 border: 1px solid var(--map-status-error-symbol-0);
240 }
241 }
242 }
243 }
244
245 &.is-warning {
246 .ivyforms-input {
247 &.el-input {
248 .el-input__wrapper {
249 border: 1px solid var(--map-status-warning-stroke-0);
250 }
251 }
252 }
253 }
254
255 // Textarea
256 &.ivyforms-form-item-textarea {
257 .ivyforms-textarea {
258 display: flex;
259 flex-direction: column;
260 align-items: flex-start;
261 font-size: 16px;
262 font-style: normal;
263 font-weight: 400;
264 line-height: 22px;
265
266 // Element Textarea
267 .el-textarea__inner {
268 border-radius: var(--Radius-radius-md, 8px);
269 border: 1px solid var(--map-base-dusk-stroke--2);
270 background-color: var(--map-base-dusk-o05);
271 display: flex;
272 align-items: center;
273 gap: var(--Spacing-sm, 8px);
274 align-self: stretch;
275 box-shadow: none;
276 color: var(--map-base-text-0);
277
278 // Hover
279 &:hover {
280 border: 1px solid var(--map-base-dusk-stroke-0);
281 background: var(--map-base-dusk-o05);
282 }
283
284 // Active & Focus
285 &:active,
286 &:focus,
287 &.is-focus,
288 &.is-active {
289 border: 1px solid var(--map-base-brand-stroke-0);
290 background: var(--map-base-dusk-o05);
291 box-shadow: none;
292 }
293 }
294 }
295
296 // Secondary
297 &.is-secondary {
298 .ivyforms-textarea {
299 .el-textarea__inner {
300 // Active & Focus
301 &:active,
302 &:focus,
303 &.is-focus,
304 &.is-active {
305 border: 1px solid var(--map-base-purple-stroke-0);
306 }
307 }
308 }
309 }
310 }
311
312 // Error and warning states
313 &.is-error {
314 .ivyforms-textarea {
315 .el-textarea__inner {
316 border: 1px solid var(--map-status-error-symbol-0);
317 }
318 }
319 }
320
321 &.is-warning {
322 .ivyforms-textarea {
323 .el-textarea__inner {
324 border: 1px solid var(--map-status-warning-stroke-0);
325 }
326 }
327 }
328 }
329 </style>
330