PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.31
Booking for Appointments and Events Calendar – Amelia v1.2.31
2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / v3 / src / views / _components / input / AmInput.vue
ameliabooking / v3 / src / views / _components / input Last commit date
AmInput.vue 1 year ago
AmInput.vue
560 lines
1 <template>
2 <div class="am-input-wrapper" :style="cssVars">
3 <el-input
4 v-bind="filteredProps"
5 ref="amInput"
6 v-model="model"
7 :class="[
8 props.type === 'text' ||
9 props.type === 'email' ||
10 props.type === 'password' ? 'am-input' : 'am-textarea',
11 props.size && (props.type !== 'text-area' && props.type !== 'textarea') ? `am-input--${props.size}` : ''
12 ]"
13 :type="props.type === 'text-area' ? 'textarea' : props.type"
14 :aria-label="props.ariaLabel"
15 @blur="(e) => $emit('blur', e)"
16 @focus="(e) => $emit('focus', e)"
17 @change="(currentValue, oldValue) => $emit('change', currentValue, oldValue)"
18 @input="(currentValue, oldValue) => $emit('input', currentValue, oldValue)"
19 @keyup.enter="(e) => $emit('enter', e)"
20 @clear="() => $emit('clear')"
21 >
22 <!-- * Icon Start/Prefix * -->
23 <template v-if="props.prefixIcon" #prefix>
24 <span
25 v-if="typeof props.prefixIcon === 'string'"
26 :class="`am-icon-${props.prefixIcon}`"
27 />
28 <component
29 :is="props.prefixIcon"
30 v-if="typeof props.prefixIcon === 'object'"
31 />
32 </template>
33 <!-- */ Icon Start/Prefix * -->
34
35 <!-- * Icon End/Suffix * -->
36 <template v-if="props.suffixIcon" #suffix>
37 <span
38 v-if="typeof props.suffixIcon === 'string'"
39 :class="`am-icon-${props.suffixIcon}`"
40 />
41 <component
42 :is="props.suffixIcon"
43 v-if="typeof props.suffixIcon === 'object'"
44 />
45 </template>
46 <!-- */ Icon End/Suffix * -->
47 </el-input>
48 </div>
49 </template>
50
51 <script setup>
52 // * Import from Vue
53 import {
54 computed,
55 ref,
56 toRefs,
57 inject
58 } from 'vue'
59
60 // * Import from Libraries
61 import {format, unformat} from 'v-money3'
62
63 // * Composables
64 import { useColorTransparency } from '../../../assets/js/common/colorManipulation'
65 import { useCurrencyOptions } from '../../../assets/js/common/formatting'
66
67 /**
68 * Component Props
69 */
70 const props = defineProps({
71 id: {
72 type: String,
73 },
74 type: {
75 type: String,
76 default: 'text',
77 },
78 modelValue: {
79 type: [String, Number, null, undefined],
80 },
81 maxlength: {
82 type: [String, Number],
83 },
84 minlength: {
85 type: [String, Number],
86 },
87 showWordLimit: {
88 // * whether show word count, only works when type is 'text' or 'textarea'
89 type: Boolean,
90 default: false
91 },
92 placeholder: {
93 type: String,
94 default: ''
95 },
96 clearable: {
97 // * whether to show clear button, only works when type is not 'textarea'
98 type: Boolean,
99 default: false
100 },
101 formatter: {
102 // * specifies the format of the value presented input.(only works when type is 'text')
103 type: Function
104 },
105 parser: {
106 // * specifies the value extracted from formatter input.(only works when type is 'text')
107 type: Function
108 },
109 showPassword: {
110 // * whether to show toggleable password input, only works when type is 'password'
111 type: Boolean,
112 default: false
113 },
114 disabled: {
115 // * whether to disable input
116 type: Boolean,
117 default: false
118 },
119 size: {
120 type: String,
121 default: 'default',
122 validator(value) {
123 return ['default', 'medium', 'small', 'mini', 'micro'].includes(value)
124 },
125 },
126 prefixIcon: {
127 type: [String, Object],
128 default: ''
129 },
130 suffixIcon: {
131 type: [String, Object],
132 default: ''
133 },
134 rows: {
135 // * number of rows of textarea, only works when type is 'textarea'
136 type: Number,
137 default: 2
138 },
139 autosize: {
140 // * whether textarea has an adaptive height, only works when type is 'textarea'. Can accept an object, e.g. { minRows: 2, maxRows: 6 }
141 type: [Boolean, Object],
142 default: false
143 },
144 autocomplete: {
145 // * whether to enable native autocomplete
146 type: String,
147 default: 'off'
148 },
149 name: {
150 // * native name attribute
151 type: String,
152 default: ''
153 },
154 readonly: {
155 type: Boolean,
156 default: false
157 },
158 max: {
159 type: [String, Number],
160 },
161 min: {
162 type: [String, Number],
163 },
164 step: {
165 type: [String, Number],
166 },
167 resize: {
168 // * whether to enable native resize, only works when type is 'textarea'
169 type: String,
170 default: 'vertical',
171 validator(value) {
172 return ['none', 'both', 'horizontal', 'vertical'].includes(value)
173 },
174 },
175 autofocus: {
176 // * whether to focus input on mounted
177 type: Boolean,
178 default: false
179 },
180 form: {
181 // * native form attribute
182 type: String,
183 },
184 ariaLabel: {
185 type: String,
186 default: ''
187 },
188 tabindex: {
189 // * native tabindex attribute
190 type: [String, Number],
191 },
192 validateEvent: {
193 type: Boolean,
194 default: true
195 },
196 inputStyle: {
197 // * custom input style
198 type: [String, Object],
199 default: () => ({})
200 },
201 isMoney: {
202 type: Boolean,
203 default: false
204 }
205 })
206
207 // Create filtered properties
208 const filteredProps = computed(() => {
209 // Create a copy of props
210 const filterObj = {...props};
211
212 // List of props to exclude
213 const excludeProps = ['id', 'type', 'modelValue', 'size', 'label', 'prefixIcon', 'suffixIcon', 'isMoney'];
214
215 // Remove excluded props
216 excludeProps.forEach(prop => {
217 delete filterObj[prop];
218 });
219
220 return filterObj;
221 });
222
223 /**
224 * Component Emits
225 * */
226 const emits = defineEmits([
227 'change',
228 'input',
229 'visible-change',
230 'clear',
231 'blur',
232 'focus',
233 'update:modelValue',
234 'enter',
235 ])
236
237 /**
238 * Component model
239 */
240 let {modelValue} = toRefs(props)
241
242 let model = computed({
243 get: () => {
244 return props.isMoney
245 ? format(modelValue.value, useCurrencyOptions())
246 : modelValue.value
247 },
248 set: (val) => {
249 emits(
250 'update:modelValue',
251 props.isMoney ? unformat(val, {...useCurrencyOptions(), modelModifiers: {number: true},}) : val
252 )
253 },
254 })
255
256 // * Color Vars
257 let amColors = inject(
258 'amColors',
259 ref({
260 colorPrimary: '#1246D6',
261 colorSuccess: '#019719',
262 colorError: '#B4190F',
263 colorWarning: '#CCA20C',
264 colorMainBgr: '#FFFFFF',
265 colorMainHeadingText: '#33434C',
266 colorMainText: '#1A2C37',
267 colorSbBgr: '#17295A',
268 colorSbText: '#FFFFFF',
269 colorInpBgr: '#FFFFFF',
270 colorInpBorder: '#D1D5D7',
271 colorInpText: '#1A2C37',
272 colorInpPlaceHolder: '#808A90',
273 colorDropBgr: '#FFFFFF',
274 colorDropBorder: '#D1D5D7',
275 colorDropText: '#0E1920',
276 colorBtnPrim: '#265CF2',
277 colorBtnPrimText: '#FFFFFF',
278 colorBtnSec: '#1A2C37',
279 colorBtnSecText: '#FFFFFF',
280 })
281 )
282
283 // * Css Variables
284 let cssVars = computed(() => {
285 return {
286 '--am-c-inp-bgr': amColors.value.colorInpBgr,
287 '--am-c-inp-border': amColors.value.colorInpBorder,
288 '--am-c-inp-text': amColors.value.colorInpText,
289 '--am-c-inp-text-op03': useColorTransparency(
290 amColors.value.colorInpText,
291 0.03
292 ),
293 '--am-c-inp-text-op05': useColorTransparency(
294 amColors.value.colorInpText,
295 0.05
296 ),
297 '--am-c-inp-text-op40': useColorTransparency(
298 amColors.value.colorInpText,
299 0.4
300 ),
301 '--am-c-inp-text-op60': useColorTransparency(
302 amColors.value.colorInpText,
303 0.6
304 ),
305 '--am-c-inp-placeholder': amColors.value.colorInpPlaceHolder,
306 }
307 })
308
309 /**
310 * Component reference
311 */
312 const amInput = ref(null)
313 </script>
314
315 <style lang="scss">
316 @mixin am-input-block {
317 // Input Wrapper
318 .am-input-wrapper {
319 --am-c-input-bgr: var(--am-c-inp-bgr);
320 --am-c-input-border: var(--am-c-inp-border);
321 --am-c-input-text: var(--am-c-inp-text);
322 --am-c-input-placeholder: var(--am-c-inp-placeholder);
323 --am-c-input-shadow: var(--am-c-inp-text-op05);
324 --am-rad-input: var(--am-rad-inp);
325 --am-fs-input: var(--am-fs-inp);
326 --am-h-input: var(--am-h-inp);
327 --am-padd-input: 0 12px;
328 display: flex;
329 width: 100%;
330 position: relative;
331
332 // Input
333 .am-input {
334 width: 100%;
335 box-sizing: border-box;
336 min-width: 100%;
337 max-width: 100%;
338 background-color: transparent;
339 box-shadow: 0 2px 2px var(--am-c-input-shadow);
340
341 &.el-input {
342 // Disabled state
343 &.is-disabled {
344 --am-c-input-bgr: var(--am-c-inp-text-op03);
345 --am-c-input-text: var(--am-c-inp-text-op60);
346 box-shadow: none;
347 cursor: not-allowed;
348
349 .el-input {
350 &__wrapper {
351 &:hover {
352 --am-c-input-border: var(--am-c-inp-border);
353 }
354 }
355 }
356 }
357
358 // Icons - Prefix and Suffix
359 &--prefix {
360 --am-padd-input: 0 12px 0 8px;
361 }
362
363 &--suffix {
364 --am-padd-input: 0 8px 0 12px;
365 }
366
367 &--prefix.el-input--suffix {
368 --am-padd-input: 0 8px;
369 }
370 }
371
372 .el-input {
373 // Input wrapper - visual display
374 &__wrapper {
375 display: inline-flex;
376 gap: 0 6px;
377 height: var(--am-input-height);
378 background-color: var(--am-c-input-bgr);
379 border: none;
380 border-radius: var(--am-rad-input);
381 box-shadow: 0 0 0 1px var(--am-c-input-border);
382 padding: var(--am-padd-input);
383 box-sizing: border-box;
384 transition: box-shadow 0.3s ease-in-out;
385
386 &:hover {
387 --am-c-input-border: var(--am-c-inp-text-op40);
388 }
389
390 &.is-focus {
391 --am-c-input-border: var(--am-c-primary);
392 }
393 }
394
395 // Input
396 &__inner {
397 width: 100%;
398 height: 100%;
399 min-height: 24px;
400 max-height: unset;
401 font-size: var(--am-fs-input);
402 line-height: 24px;
403 color: var(--am-c-input-text);
404 border: none;
405 background: none;
406 border-radius: 0;
407 padding: 0;
408 margin: 0;
409 box-shadow: none;
410
411 // Placeholder
412 &::placeholder {
413 color: var(--am-c-input-placeholder);
414 opacity: 1; /* Ensures it’s not transparent */
415 }
416 &::-webkit-input-placeholder { /* Chrome, Safari */
417 color: var(--am-c-input-placeholder);
418 }
419 &:-moz-placeholder { /* Firefox 4-18 */
420 color: var(--am-c-input-placeholder);
421 opacity: 1;
422 }
423 &::-moz-placeholder { /* Firefox 19+ */
424 color: var(--am-c-input-placeholder);
425 opacity: 1;
426 }
427 &:-ms-input-placeholder { /* IE 10-11 */
428 color: var(--am-c-input-placeholder);
429 }
430 }
431
432 // Prefix and Suffix Icons
433 &__prefix, &__suffix {
434 &-inner {
435 align-items: center;
436 font-size: 24px;
437 color: var(--am-c-inp-text);
438
439 * {
440 font-size: 24px;
441 color: var(--am-c-inp-text);
442 }
443
444 // Element plus icons
445 i.el-input__icon {
446 font-size: 18px;
447
448 // Clear icon - override
449 &.el-input__clear {
450 &:before {
451 font-family: 'amelia-icons' !important;
452 content: $am-icon-close;
453 }
454
455 svg {
456 display: none;
457 }
458 }
459 }
460 }
461 }
462 }
463
464 // Input Sizes - Default, Medium, Small, Mini, Micro
465 &--default {
466 --am-input-height: 40px;
467 }
468 &--medium {
469 --am-input-height: 36px;
470 }
471 &--small {
472 --am-input-height: 32px;
473 }
474 &--mini {
475 --am-input-height: 28px;
476 }
477 &--micro {
478 --am-input-height: 24px;
479 }
480 }
481
482 // Textarea
483 .am-textarea {
484 width: 100%;
485 box-sizing: border-box;
486 min-width: 100%;
487 max-width: 100%;
488 box-shadow: 0 2px 2px var(--am-c-input-shadow);
489
490 &.is-disabled {
491 --am-c-input-bgr: var(--am-c-inp-text-op03);
492 --am-c-input-text: var(--am-c-inp-text-op60);
493 box-shadow: none;
494 cursor: not-allowed;
495
496 .el-textarea {
497 &__inner {
498 &:hover {
499 --am-c-input-border: var(--am-c-inp-border);
500 }
501 }
502 }
503 }
504
505 .el-textarea {
506 &__inner {
507 width: 100%;
508 color: var(--am-c-input-text);
509 background-color: var(--am-c-input-bgr);
510 border: none;
511 border-radius: var(--am-rad-input);
512 box-shadow: 0 0 0 1px var(--am-c-input-border);
513 padding: 8px 12px;
514 box-sizing: border-box;
515 transition: box-shadow 0.3s ease-in-out;
516
517 &:hover {
518 --am-c-input-border: var(--am-c-inp-text-op40);
519 }
520
521 &.is-focus {
522 --am-c-input-border: var(--am-c-primary);
523 }
524
525 // Placeholder
526 &::placeholder {
527 color: var(--am-c-input-placeholder);
528 opacity: 1; /* Ensures it’s not transparent */
529 }
530 &::-webkit-input-placeholder { /* Chrome, Safari */
531 color: var(--am-c-input-placeholder);
532 }
533 &:-moz-placeholder { /* Firefox 4-18 */
534 color: var(--am-c-input-placeholder);
535 opacity: 1;
536 }
537 &::-moz-placeholder { /* Firefox 19+ */
538 color: var(--am-c-input-placeholder);
539 opacity: 1;
540 }
541 &:-ms-input-placeholder { /* IE 10-11 */
542 color: var(--am-c-input-placeholder);
543 }
544 }
545 }
546 }
547 }
548 }
549
550 // public
551 .amelia-v2-booking #amelia-container {
552 @include am-input-block;
553 }
554
555 // admin
556 #amelia-app-backend-new {
557 @include am-input-block;
558 }
559 </style>
560