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