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

338 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?:
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 // Secondary
207 &.is-secondary {
208 .el-input__wrapper {
209 // Active & Focus
210 &:active,
211 &:focus,
212 &.is-focus,
213 &.is-active {
214 border: 1px solid var(--map-base-purple-stroke-0);
215 }
216 }
217 }
218 }
219 }
220
221 // Form Item
222 .el-form-item {
223 &.ivyforms-form-item-input {
224 // Secondary
225 &.is-secondary {
226 .ivyforms-input {
227 &.el-input {
228 .el-input__wrapper {
229 // Active & Focus
230 &:active,
231 &:focus,
232 &.is-focus,
233 &.is-active {
234 border: 1px solid var(--map-base-purple-stroke-0);
235 }
236 }
237 }
238 }
239 }
240 }
241
242 // Error and warning states
243 &.is-error {
244 .ivyforms-input {
245 &.el-input {
246 .el-input__wrapper {
247 border: 1px solid var(--map-status-error-symbol-0);
248 }
249 }
250 }
251 }
252
253 &.is-warning {
254 .ivyforms-input {
255 &.el-input {
256 .el-input__wrapper {
257 border: 1px solid var(--map-status-warning-stroke-0);
258 }
259 }
260 }
261 }
262
263 // Textarea
264 &.ivyforms-form-item-textarea {
265 .ivyforms-textarea {
266 display: flex;
267 flex-direction: column;
268 align-items: flex-start;
269 font-size: 16px;
270 font-style: normal;
271 font-weight: 400;
272 line-height: 22px;
273
274 // Element Textarea
275 .el-textarea__inner {
276 border-radius: var(--Radius-radius-md, 8px);
277 border: 1px solid var(--map-base-dusk-stroke--2);
278 background-color: var(--map-base-dusk-o05);
279 display: flex;
280 align-items: center;
281 gap: var(--Spacing-sm, 8px);
282 align-self: stretch;
283 box-shadow: none;
284 color: var(--map-base-text-0);
285
286 // Hover
287 &:hover {
288 border: 1px solid var(--map-base-dusk-stroke-0);
289 background: var(--map-base-dusk-o05);
290 }
291
292 // Active & Focus
293 &:active,
294 &:focus,
295 &.is-focus,
296 &.is-active {
297 border: 1px solid var(--map-base-brand-stroke-0);
298 background: var(--map-base-dusk-o05);
299 box-shadow: none;
300 }
301 }
302 }
303
304 // Secondary
305 &.is-secondary {
306 .ivyforms-textarea {
307 .el-textarea__inner {
308 // Active & Focus
309 &:active,
310 &:focus,
311 &.is-focus,
312 &.is-active {
313 border: 1px solid var(--map-base-purple-stroke-0);
314 }
315 }
316 }
317 }
318 }
319
320 // Error and warning states
321 &.is-error {
322 .ivyforms-textarea {
323 .el-textarea__inner {
324 border: 1px solid var(--map-status-error-symbol-0);
325 }
326 }
327 }
328
329 &.is-warning {
330 .ivyforms-textarea {
331 .el-textarea__inner {
332 border: 1px solid var(--map-status-warning-stroke-0);
333 }
334 }
335 }
336 }
337 </style>
338