PluginProbe
The Innovative Form Builder – IvyForms / 0.4
The Innovative Form Builder – IvyForms v0.4
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.4, at frontend/src/views/_components/input/IvyTextInput.vue

363 lines 8.0 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?:
59 | 'text'
60 | 'textarea'
61 | 'number'
62 | 'select'
63 | 'multiple-select'
64 | 'phone'
65 | 'email'
66 | 'website'
67 placeholder?: string
68 iconStart?: string
69 iconEnd?: string
70 iconStartCategory?: IconCategory
71 iconEndCategory?: IconCategory
72 avatar?: string
73 iconStartType?: IconType
74 iconEndType?: IconType
75 filled?: boolean
76 secondary?: boolean
77 prefix?: string
78 suffix?: string
79 clearable?: boolean
80 modelValue: string | number | null | undefined
81 }
82
83 const props = withDefaults(defineProps<Props>(), {
84 type: 'text',
85 placeholder: '',
86 iconStart: '',
87 iconEnd: '',
88 iconStartCategory: 'global',
89 iconEndCategory: 'global',
90 iconStartType: 'outline',
91 iconEndType: 'outline',
92 avatar: '',
93 prefix: '',
94 suffix: '',
95 })
96
97 const emit = defineEmits(['update:modelValue'])
98
99 const localModelValue = ref(props.modelValue)
100 const localInputType = ref(props.type)
101
102 const slots = useSlots()
103
104 const elInputRef = ref()
105 const isFocused = ref(false)
106
107 // Sync localModelValue with props.modelValue
108 watch(
109 () => props.modelValue,
110 (newValue) => {
111 localModelValue.value = newValue
112 },
113 )
114
115 // Emit update:modelValue when localModelValue changes
116 watch(localModelValue, (newValue) => {
117 emit('update:modelValue', newValue)
118 })
119
120 const clearInput = () => {
121 localModelValue.value = ''
122 }
123
124 const focus = () => {
125 elInputRef.value?.focus()
126 }
127
128 defineExpose({
129 focus,
130 })
131 </script>
132
133 <style lang="scss">
134 // Input
135 .ivyforms-input {
136 &.el-input {
137 height: 40px;
138
139 .el-input__wrapper {
140 display: flex;
141 align-items: center;
142 gap: var(--Spacing-sm, 8px);
143 padding: var(--Spacing-sm, 4px) var(--Spacing-lg, 12px);
144 align-self: stretch;
145 border-radius: var(--Radius-radius-md, 8px);
146 border: 1px solid var(--map-base-dusk-stroke--2);
147 background: var(--map-base-dusk-o05);
148 box-shadow: none;
149
150 .el-input__prefix-inner > :last-child {
151 margin-right: 0;
152 }
153
154 // Hover
155 &:hover,
156 &.is-hover {
157 border: 1px solid var(--map-base-dusk-stroke-0);
158 background: var(--map-base-dusk-o05);
159 }
160
161 // Inner input
162 input {
163 padding: 0;
164 font-size: 16px;
165 font-style: normal;
166 font-weight: 400;
167 line-height: 22px;
168 color: var(--map-base-text-0);
169 border: none;
170 box-shadow: none;
171 background-color: transparent;
172 display: flex;
173 align-items: center;
174 gap: 4px;
175 flex: 1 0 0;
176 }
177
178 // Active & Focus
179 &:active,
180 &:focus,
181 &.is-focus,
182 &.is-active {
183 border: 1px solid var(--map-base-brand-stroke-0);
184 background: var(--map-base-dusk-o05);
185 box-shadow: none;
186 }
187
188 // Clear Icon
189 .ivyforms-input__clear {
190 cursor: pointer;
191 width: 20px;
192 height: 20px;
193 transform: scale(1);
194 margin: 0;
195 }
196
197 .el-input__clear {
198 display: none !important;
199
200 &::before {
201 display: none !important;
202 }
203 }
204 }
205
206 // Disabled state
207 &.is-disabled {
208 .el-input__wrapper {
209 background: var(--map-base-dusk-o05);
210 border-color: var(--map-base-dusk-stroke--2);
211 box-shadow: none;
212
213 input {
214 color: var(--map-base-text-0);
215 opacity: 0.5;
216 }
217 }
218 }
219
220 // Secondary
221 &.is-secondary {
222 .el-input__wrapper {
223 // Active & Focus
224 &:active,
225 &:focus,
226 &.is-focus,
227 &.is-active {
228 border: 1px solid var(--map-base-purple-stroke-0);
229 }
230 }
231 }
232 }
233 }
234
235 // Form Item
236 .el-form-item {
237 &.ivyforms-form-item-input {
238 // Secondary
239 &.is-secondary {
240 .ivyforms-input {
241 &.el-input {
242 .el-input__wrapper {
243 // Active & Focus
244 &:active,
245 &:focus,
246 &.is-focus,
247 &.is-active {
248 border: 1px solid var(--map-base-purple-stroke-0);
249 }
250 }
251 }
252 }
253 }
254 }
255
256 // Error and warning states
257 &.is-error {
258 .ivyforms-input {
259 &.el-input {
260 .el-input__wrapper {
261 border: 1px solid var(--map-status-error-symbol-0) !important;
262 box-shadow: none !important;
263 }
264
265 .el-input__wrapper:hover,
266 .el-input__wrapper:focus,
267 .el-input__wrapper:active,
268 .el-input__wrapper.is-focus,
269 .el-input__wrapper.is-active {
270 border: 1px solid var(--map-status-error-symbol-0) !important;
271 box-shadow: none !important;
272 }
273 }
274 }
275 }
276
277 &.is-warning {
278 .ivyforms-input {
279 &.el-input {
280 .el-input__wrapper {
281 border: 1px solid var(--map-status-warning-stroke-0);
282 }
283 }
284 }
285 }
286
287 // Textarea
288 &.ivyforms-form-item-textarea {
289 .ivyforms-textarea {
290 display: flex;
291 flex-direction: column;
292 align-items: flex-start;
293 font-size: 16px;
294 font-style: normal;
295 font-weight: 400;
296 line-height: 22px;
297
298 // Element Textarea
299 .el-textarea__inner {
300 border-radius: var(--Radius-radius-md, 8px);
301 border: 1px solid var(--map-base-dusk-stroke--2);
302 background-color: var(--map-base-dusk-o05);
303 display: flex;
304 align-items: center;
305 gap: var(--Spacing-sm, 8px);
306 align-self: stretch;
307 box-shadow: none;
308 color: var(--map-base-text-0);
309
310 // Hover
311 &:hover {
312 border: 1px solid var(--map-base-dusk-stroke-0);
313 background: var(--map-base-dusk-o05);
314 }
315
316 // Active & Focus
317 &:active,
318 &:focus,
319 &.is-focus,
320 &.is-active {
321 border: 1px solid var(--map-base-brand-stroke-0);
322 background: var(--map-base-dusk-o05);
323 box-shadow: none;
324 }
325 }
326 }
327
328 // Secondary
329 &.is-secondary {
330 .ivyforms-textarea {
331 .el-textarea__inner {
332 // Active & Focus
333 &:active,
334 &:focus,
335 &.is-focus,
336 &.is-active {
337 border: 1px solid var(--map-base-purple-stroke-0);
338 }
339 }
340 }
341 }
342 }
343
344 // Error and warning states
345 &.is-error {
346 .ivyforms-textarea {
347 .el-textarea__inner {
348 border: 1px solid var(--map-status-error-symbol-0) !important;
349 box-shadow: 0 0 0 1px var(--el-color-danger) inset !important;
350 }
351 }
352 }
353
354 &.is-warning {
355 .ivyforms-textarea {
356 .el-textarea__inner {
357 border: 1px solid var(--map-status-warning-stroke-0);
358 }
359 }
360 }
361 }
362 </style>
363