PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.8
Booking for Appointments and Events Calendar – Amelia v2.4.8
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 / public / EventForm / Common / EventInfo / EventInfo.vue
ameliabooking / v3 / src / views / public / EventForm / Common / EventInfo Last commit date
EventInfo.vue 1 month ago EventInfoSkeleton.vue 1 month ago
EventInfo.vue
872 lines
1 <template>
2 <!-- Event List (elf) -->
3 <div
4 v-if="!loading"
5 ref="eventInfoRef"
6 class="am-eli"
7 :class="props.globalClass"
8 :style="cssVars"
9 role="region"
10 :aria-label="selectedEvent.name || amLabels.event_info"
11 @keydown.capture="onKeydownCapture"
12 >
13 <!-- Gallery -->
14 <GalleryCarousel
15 v-if="selectedEvent.gallery.length && customizedOptions.gallery.visibility"
16 :gallery="selectedEvent.gallery"
17 class="am-eli__image"
18 :aria-label="amLabels.event_info + ' ' + amLabels.gallery"
19 role="img"
20 ></GalleryCarousel>
21 <!--/ Gallery -->
22
23 <!-- Event header summary -->
24 <div class="am-eli__header" role="group" :aria-label="amLabels.event_info">
25 <EventCard
26 :event="selectedEvent"
27 :labels="amLabels"
28 :locations="locations"
29 :tickets="tickets"
30 :customized-options="customizedOptions"
31 :date-string-visibility="true"
32 :border-visibility="false"
33 :btn-visibility="false"
34 :auto-info-height="true"
35 :image-visibility="false"
36 vertical-orientation="start"
37 :in-header="true"
38 :in-dialog="true"
39 :tax-visible="
40 useTaxVisibility(store, selectedEvent.id, 'event') &&
41 (customizedOptions.tax?.visibility ?? true)
42 "
43 ></EventCard>
44 </div>
45 <!--/ Event header summary -->
46
47 <!-- Main tabbed content -->
48 <div class="am-eli__main">
49 <AmTabs v-model="activeName" :aria-label="amLabels.event_info">
50 <!-- Info tab -->
51 <AmTabsItem :label="amLabels.event_info" name="first">
52 <!-- Timetable -->
53 <div v-if="selectedEvent.periods.length" class="am-eli__timetable am-eli__main-item">
54 <p id="am-eli-timetable-title" class="am-eli__timetable-title am-eli__main-title">
55 {{ amLabels.event_timetable }}
56 <span v-if="eventRange.start !== eventRange.end">
57 {{ `: ${eventRange.start} - ${eventRange.end}` }}
58 </span>
59 </p>
60 <ul class="am-eli__timetable-list" aria-labelledby="am-eli-timetable-title" role="list">
61 <li
62 v-for="(period, index) in selectedEventsPeriods"
63 :key="`${period.dateOfMonth}-${index}`"
64 class="am-eli__timetable-main"
65 >
66 <span class="am-eli__timetable-main__date am-eli__main-text">
67 {{ period.dateOfMonth }}
68 </span>
69 <span
70 class="am-eli__timetable-main__time am-eli__main-text"
71 :aria-label="`${period.dateOfMonth}, ${period.timeStart} – ${period.timeEnd}`"
72 >
73 {{ ` - ${period.timeStart} - ${period.timeEnd}` }}
74 </span>
75 </li>
76 </ul>
77 </div>
78 <!--/ Timetable -->
79
80 <DescriptionItem
81 v-if="customizedOptions.eventDescription.visibility"
82 class="am-eli__main-item"
83 :title="amLabels.description"
84 :description="selectedEvent.description"
85 :limit="250"
86 :customized-labels="amLabels"
87 ></DescriptionItem>
88
89 <!-- Organizer -->
90 <EventEmployee
91 v-if="customizedOptions.eventOrganizer.visibility"
92 :divider="true"
93 :employee="eventOrganizer"
94 :rank="amLabels.event_organizer"
95 :customized-labels="amLabels"
96 class="am-eli__main-item"
97 role="group"
98 :aria-label="amLabels.event_organizer"
99 ></EventEmployee>
100 <!--/ Organizer -->
101
102 <!-- Staff -->
103 <template v-if="customizedOptions.eventEmployees.visibility">
104 <EventEmployee
105 v-for="employee in eventStaff"
106 :key="employee.id"
107 :divider="true"
108 :employee="employee"
109 :customized-labels="amLabels"
110 class="am-eli__main-item"
111 role="group"
112 :aria-label="employee.firstName ? `${employee.firstName} ${employee.lastName}` : ''"
113 ></EventEmployee>
114 </template>
115 <!--/ Staff -->
116 </AmTabsItem>
117 <!--/ Info tab -->
118
119 <!-- Tickets tab -->
120 <AmTabsItem v-if="tickets.length" :label="amLabels.event_tickets" name="second">
121 <template v-for="ticket in tickets" :key="ticket.id">
122 <EventTicket
123 :ticket="ticket"
124 :capacity="
125 !isWaitingList
126 ? selectedEvent.maxCustomCapacity
127 : selectedEvent.maxCustomCapacity
128 ? waitingListOptions.maxCapacity
129 : null
130 "
131 :extra-people="
132 !isWaitingList
133 ? selectedEvent.maxExtraPeople
134 : waitingListOptions.maxExtraPeopleEnabled
135 ? waitingListOptions.maxExtraPeople
136 : null
137 "
138 :customized-labels="amLabels"
139 :readonly="true"
140 />
141 </template>
142 </AmTabsItem>
143 <!--/ Tickets tab -->
144 </AmTabs>
145 </div>
146 <!--/ Main tabbed content -->
147 </div>
148
149 <!-- Loading skeleton -->
150 <EventInfoSkeleton
151 v-else
152 aria-busy="true"
153 :aria-label="
154 amLabels.loading_event_information || amLabels.loading || 'Loading event information'
155 "
156 role="status"
157 ></EventInfoSkeleton>
158 <!--/ Loading skeleton -->
159
160 <!-- Bringing Anyone with you -->
161 <AmSlidePopup
162 v-if="bringingAnyoneAvailability"
163 :visibility="bringingAnyoneVisibility"
164 custom-class="am-eli__bringing-wrapper"
165 role="dialog"
166 aria-modal="true"
167 :aria-label="amDialogLabels.bringing_anyone || amLabels.bringing_anyone"
168 :aria-hidden="!bringingAnyoneVisibility"
169 >
170 <BringingAnyone></BringingAnyone>
171
172 <template #footer>
173 <div class="am-eli__bringing-footer" :class="responsiveClass">
174 <AmButton
175 :type="amDialogCustomizedOptions.secBtn.buttonType"
176 category="secondary"
177 :disabled="false"
178 :aria-label="amDialogLabels.back_btn"
179 @click="noOneBringWith"
180 >
181 {{ amDialogLabels.back_btn }}
182 </AmButton>
183 <AmButton
184 :type="amDialogCustomizedOptions.primBtn.buttonType"
185 :category="isWaitingList ? 'waiting' : 'primary'"
186 :disabled="false"
187 :aria-label="amDialogLabels.continue"
188 @click="bringPeopleWithYou"
189 >
190 {{ amDialogLabels.continue }}
191 </AmButton>
192 </div>
193 </template>
194 </AmSlidePopup>
195 <!--/ Bringing Anyone with you -->
196 </template>
197
198 <script setup>
199 import AmTabs from '../../../../_components/tabs/AmTabs.vue'
200 import AmTabsItem from '../../../../_components/tabs/AmTabsItem.vue'
201 // * _components
202 import AmButton from '../../../../_components/button/AmButton.vue'
203 import AmSlidePopup from '../../../../_components/slide-popup/AmSlidePopup.vue'
204
205 // * Dedicated Components
206 import EventCard from '../../Common/Parts/EventCard.vue'
207 import DescriptionItem from '../../Common/Parts/DescriptionItem.vue'
208 import EventEmployee from '../../Common/Parts/EventEmployee.vue'
209 import EventInfoSkeleton from './EventInfoSkeleton.vue'
210
211 // * Parts
212 import EventTicket from '../../Common/Parts/EventTicket.vue'
213 import GalleryCarousel from '../../Common/Parts/GalleryCarousel.vue'
214
215 // * Dialogs
216 import BringingAnyone from '../../Common/Parts/BringingAnyone.vue'
217
218 // * Import from Vue
219 import { inject, ref, reactive, computed, onMounted, nextTick, watch, watchEffect } from 'vue'
220
221 // * Import from Vuex
222 import { useStore } from 'vuex'
223
224 // * Composables
225 import {
226 getFrontedFormattedDate,
227 getEventFrontedFormattedTime,
228 } from '../../../../../assets/js/common/date'
229 import { useColorTransparency } from '../../../../../assets/js/common/colorManipulation'
230 import { useResponsiveClass } from '../../../../../assets/js/common/responsive'
231 import { useTaxVisibility } from '../../../../../assets/js/common/pricing'
232 import useAction from '../../../../../assets/js/public/actions'
233 import moment from 'moment/moment'
234
235 // * Components Properties
236 let props = defineProps({
237 globalClass: {
238 type: String,
239 default: '',
240 },
241 inDialog: {
242 type: Boolean,
243 default: false,
244 },
245 })
246
247 // * Step functionality
248 let {
249 nextStep,
250 headerButtonPreviousReset,
251 footerButtonReset,
252 footerButtonClicked,
253 footerBtnDisabled,
254 } = inject('changingStepsFunctions', {
255 nextStep: () => {},
256 headerButtonPreviousReset: () => {},
257 footerButtonReset: () => {},
258 footerButtonClicked: ref(false),
259 footerBtnDisabled: ref(false),
260 })
261
262 // * Define store
263 const store = useStore()
264
265 // * Loading state
266 let loading = computed(() => store.getters['getLoading'])
267
268 // * Root Settings
269 const amSettings = inject('settings')
270
271 // * Event form customization
272 let customizedDataForm = inject('customizedDataForm')
273
274 // * Customization options
275 let customizedOptions = computed(() => {
276 return customizedDataForm.value.info.options
277 })
278
279 // * Labels
280 let labels = inject('labels')
281
282 // * local language short code
283 const localLanguage = inject('localLanguage')
284
285 // * if local lang is in settings lang
286 let langDetection = computed(() => amSettings.general.usedLanguages.includes(localLanguage.value))
287
288 // * Computed labels
289 let amLabels = computed(() => {
290 let computedLabels = reactive({ ...labels })
291
292 if (customizedDataForm.value.info.translations) {
293 let customizedLabels = customizedDataForm.value.info.translations
294 Object.keys(customizedLabels).forEach((labelKey) => {
295 if (customizedLabels[labelKey][localLanguage.value] && langDetection.value) {
296 computedLabels[labelKey] = customizedLabels[labelKey][localLanguage.value]
297 } else if (customizedLabels[labelKey].default) {
298 computedLabels[labelKey] = customizedLabels[labelKey].default
299 }
300 })
301 }
302 return computedLabels
303 })
304
305 let amDialogLabels = computed(() => {
306 let computedLabels = reactive({ ...labels })
307
308 if (customizedDataForm.value.bringingAnyone.translations) {
309 let customizedLabels = customizedDataForm.value.bringingAnyone.translations
310 Object.keys(customizedLabels).forEach((labelKey) => {
311 if (customizedLabels[labelKey][localLanguage.value] && langDetection.value) {
312 computedLabels[labelKey] = customizedLabels[labelKey][localLanguage.value]
313 } else if (customizedLabels[labelKey].default) {
314 computedLabels[labelKey] = customizedLabels[labelKey].default
315 }
316 })
317 }
318 return computedLabels
319 })
320
321 let amDialogCustomizedOptions = computed(() => {
322 return customizedDataForm.value.bringingAnyone.options
323 })
324
325 // * Event obj
326 let selectedEvent = computed(() =>
327 store.getters['eventEntities/getEvent'](store.getters['eventBooking/getSelectedEventId']),
328 )
329
330 let isWaitingList = computed(() => store.getters['eventWaitingListOptions/getAvailability'])
331
332 let waitingListOptions = computed(() => store.getters['eventWaitingListOptions/getOptions'])
333
334 let locations = computed(() => store.getters['eventEntities/getLocations'])
335
336 let employees = computed(() => store.getters['eventEntities/getEmployees'])
337
338 let selectedEventsPeriods = computed(() => {
339 let periodsArr = []
340 selectedEvent.value.periods.forEach(function (period) {
341 let periodStartDate = moment(period.periodStart.split(' ')[0], 'YYYY-MM-DD')
342 let periodEndDate = moment(period.periodEnd.split(' ')[0], 'YYYY-MM-DD')
343 let periodStartTime = moment(period.periodStart.split(' ')[1], 'HH:mm:ss').format('HH:mm:ss')
344 let periodEndTime = moment(period.periodEnd.split(' ')[1], 'HH:mm:ss').format('HH:mm:ss')
345
346 if (periodEndTime === '00:00:00') {
347 periodEndTime = '24:00:00'
348 periodEndDate.subtract(1, 'days')
349 }
350
351 /** if the period in the event lasts for several days */
352 if (periodStartDate.diff(periodEndDate, 'days') < 0) {
353 let periodDates = []
354
355 while (periodStartDate.isSameOrBefore(periodEndDate)) {
356 periodDates.push(periodStartDate.format('YYYY-MM-DD'))
357 periodStartDate.add(1, 'days')
358 }
359
360 periodDates.forEach((dayPeriod) => {
361 periodsArr.push({
362 id: period.id,
363 start: dayPeriod + ' ' + periodStartTime,
364 end: dayPeriod + ' ' + periodEndTime,
365 })
366 })
367 } else {
368 periodsArr.push({
369 id: period.id,
370 start: periodStartDate.format('YYYY-MM-DD') + ' ' + periodStartTime,
371 end: periodEndDate.format('YYYY-MM-DD') + ' ' + periodEndTime,
372 })
373 }
374 })
375
376 let periods = []
377 periodsArr
378 .sort((a, b) => moment(a.start, 'YYYY-MM-DD HH:mm:ss') - moment(b.start, 'YYYY-MM-DD HH:mm:ss'))
379 .forEach((item) => {
380 periods.push({
381 dateOfMonth: getFrontedFormattedDate(item.start.split(' ')[0]),
382 timeStart: getEventFrontedFormattedTime(item.start.split(' ')[1]),
383 timeEnd: getEventFrontedFormattedTime(item.end.split(' ')[1]),
384 })
385 })
386
387 return periods
388 })
389
390 let eventRange = computed(() => {
391 let rangeArr = []
392 let rangeObj = {}
393
394 selectedEvent.value.periods.forEach((item) => {
395 rangeArr.push(
396 moment(item.periodStart, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD'),
397 moment(item.periodEnd, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD'),
398 )
399 })
400
401 rangeArr = rangeArr.sort()
402 rangeObj.start = getFrontedFormattedDate(rangeArr[0])
403 rangeObj.end = getFrontedFormattedDate(rangeArr[rangeArr.length - 1])
404
405 return rangeObj
406 })
407
408 const activeName = ref('first')
409
410 const eventInfoRef = ref(null)
411
412 function onKeydownCapture(e) {
413 if (e?.key !== 'ArrowUp' && e?.key !== 'ArrowDown') {
414 return
415 }
416
417 const target = e.target
418 if (!(target instanceof HTMLElement)) {
419 return
420 }
421
422 // Don't interfere with arrow-key behavior inside form fields/editable content.
423 const editable = target.closest('input, textarea, select, [contenteditable="true"]')
424 if (editable) {
425 return
426 }
427
428 // Element Plus tabs header supports arrow key navigation; prevent Up/Down from switching tabs.
429 const isTabHeader = target.closest('.el-tabs__header')
430 const isTab = target.closest('[role="tab"], .el-tabs__item')
431 if (isTabHeader && isTab) {
432 e.preventDefault()
433 e.stopPropagation()
434
435 const scrollEl = eventInfoRef.value
436 if (scrollEl && typeof scrollEl.scrollBy === 'function') {
437 const delta = e.key === 'ArrowDown' ? 40 : -40
438 scrollEl.scrollBy({ top: delta, left: 0, behavior: 'auto' })
439 }
440 }
441 }
442
443 // * Footer btn
444 watchEffect(
445 () => {
446 footerBtnDisabled.value =
447 !isWaitingList.value && (!selectedEvent.value.bookable || selectedEvent.value.full)
448 },
449 { flush: 'post' },
450 )
451
452 // * Event fluid step keys
453 let eventFluidStepKey = inject('eventFluidStepKey')
454
455 // * add or remove event tickets step
456 if (selectedEvent.value.customPricing) {
457 if (eventFluidStepKey.value.indexOf('eventTickets') < 0) {
458 eventFluidStepKey.value.push('eventTickets')
459 }
460 } else {
461 let index = eventFluidStepKey.value.indexOf('eventTickets')
462 if (index > -1) {
463 eventFluidStepKey.value.splice(index, 1)
464 }
465 }
466
467 // * Event Organizer and Staff
468 let eventOrganizer = computed(() => {
469 const employeeId = selectedEvent.value.organizerId
470
471 if (!employeeId) {
472 return {}
473 }
474
475 const fromEmployees = employees.value.find((e) => e.id === employeeId)
476
477 if (fromEmployees) {
478 return fromEmployees
479 }
480
481 const organizer = selectedEvent.value.organizer
482
483 if (!organizer?.id) {
484 return {}
485 }
486
487 return {
488 ...organizer,
489 pictureThumbPath: organizer.pictureThumbPath || organizer.picture || null,
490 }
491 })
492
493 let eventStaff = computed(() => {
494 const filtered = selectedEvent.value.providers.map((provider) => {
495 const employee = employees.value.find((e) => e.id === provider.id)
496
497 if (employee) {
498 return { ...provider, badge: employee.badge }
499 }
500
501 const staffMember = selectedEvent.value.staff?.find((s) => s.id === provider.id)
502
503 return {
504 ...provider,
505 pictureThumbPath: provider.pictureThumbPath || staffMember?.picture || null,
506 badge: staffMember?.badge || null,
507 }
508 })
509
510 if (selectedEvent.value.organizerId) {
511 return filtered.filter((e) => e.id !== selectedEvent.value.organizerId)
512 }
513
514 return filtered
515 })
516
517 // * Set max people based on settings from backend
518 let evCapacity = computed(
519 () =>
520 selectedEvent.value.maxCapacity -
521 (selectedEvent.value.maxCapacity - selectedEvent.value.places),
522 )
523
524 // * Set max waiting people based on settings from backend
525 let evWaitingCapacity = computed(() => {
526 if (!waitingListOptions.value.enabled) {
527 return 0
528 }
529 return waitingListOptions.value.maxCapacity - waitingListOptions.value.peopleWaiting
530 })
531
532 watchEffect(
533 () => {
534 if (isWaitingList.value) {
535 if (
536 waitingListOptions.value.maxExtraPeopleEnabled &&
537 waitingListOptions.value.maxExtraPeople &&
538 evWaitingCapacity.value > waitingListOptions.value.maxExtraPeople
539 ) {
540 store.commit('persons/setMaxPersons', waitingListOptions.value.maxExtraPeople + 1)
541 } else {
542 store.commit('persons/setMaxPersons', evWaitingCapacity.value)
543 }
544 } else {
545 if (
546 selectedEvent.value.maxExtraPeople &&
547 evCapacity.value > selectedEvent.value.maxExtraPeople
548 ) {
549 store.commit('persons/setMaxPersons', selectedEvent.value.maxExtraPeople + 1)
550 } else {
551 store.commit('persons/setMaxPersons', evCapacity.value)
552 }
553 }
554 },
555 { flush: 'post' },
556 )
557
558 // * Bringing anyone with you slide popup
559 let bringingAnyoneAvailability = computed(() => {
560 const commonCondition =
561 !selectedEvent.value.customPricing &&
562 selectedEvent.value.bringingAnyone &&
563 store.getters['persons/getMinPersons'] !== store.getters['persons/getMaxPersons']
564
565 if (isWaitingList.value) {
566 return commonCondition && evWaitingCapacity.value > 1
567 } else {
568 return commonCondition && evCapacity.value > 1
569 }
570 })
571
572 let bringingAnyoneVisibility = ref(false)
573
574 // * Focus management – track the element that opened the dialog so we can
575 // * restore focus when it closes (WCAG 2.1 SC 2.4.3 / 3.2.2).
576 let triggerElement = ref(null)
577
578 watch(bringingAnyoneVisibility, async (isVisible) => {
579 if (isVisible) {
580 // Save current focus so we can restore it on close
581 triggerElement.value = document.activeElement
582 // Move focus into the dialog on next tick once it is rendered
583 await nextTick()
584 const dialog = document.querySelector('.am-eli__bringing-wrapper')
585 if (dialog) {
586 const focusable = dialog.querySelector(
587 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',
588 )
589 focusable?.focus()
590 }
591 } else {
592 // Restore focus to the element that opened the dialog
593 triggerElement.value?.focus()
594 triggerElement.value = null
595 }
596 })
597
598 function noOneBringWith() {
599 bringingAnyoneVisibility.value = false
600 }
601
602 function bringPeopleWithYou() {
603 nextStep()
604 }
605
606 // * Event Tickets
607 watchEffect(
608 () => {
609 if (selectedEvent.value.customPricing && !store.getters['tickets/getTicketsData'].length) {
610 store.commit('tickets/setTickets', selectedEvent.value.customTickets)
611
612 if (selectedEvent.value.maxCustomCapacity) {
613 if (isWaitingList.value) {
614 store.commit('tickets/setMaxCustomCapacity', waitingListOptions.value.maxCapacity)
615 } else {
616 store.commit('tickets/setMaxCustomCapacity', selectedEvent.value.maxCustomCapacity)
617 }
618 }
619 }
620
621 if (selectedEvent.value.maxExtraPeople && !isWaitingList.value) {
622 store.commit('tickets/setMaxExtraPeople', selectedEvent.value.maxExtraPeople)
623 }
624
625 if (isWaitingList.value && waitingListOptions.value.maxExtraPeopleEnabled) {
626 store.commit('tickets/setMaxExtraPeople', waitingListOptions.value.maxExtraPeople)
627 }
628
629 store.commit('tickets/applyPendingPreselectedTickets', { waitingMode: isWaitingList.value })
630 },
631 { flush: 'post' },
632 )
633
634 // * Event Tickets
635 let tickets = computed(() => {
636 const sorted = [...(store.getters['tickets/getTicketsData'] || [])]
637 if (window?.ameliaActions?.EventTickets) {
638 let override
639 useAction(
640 store,
641 {
642 tickets: sorted,
643 setTickets: (arr) => {
644 if (Array.isArray(arr)) override = arr
645 },
646 },
647 'EventTickets',
648 'event',
649 null,
650 null,
651 )
652 return Array.isArray(override) ? override : sorted
653 }
654 return sorted
655 })
656
657 // * Watching when footer button was clicked
658 watchEffect(() => {
659 if (footerButtonClicked.value) {
660 footerButtonReset()
661
662 if (bringingAnyoneAvailability.value) {
663 bringingAnyoneVisibility.value = true
664 } else {
665 nextStep()
666 }
667 }
668 })
669
670 onMounted(() => {
671 store.commit('payment/setPaymentDepositType', selectedEvent.value.depositPayment)
672 store.commit('payment/setPaymentDepositAmount', selectedEvent.value.deposit)
673 store.commit('setLoading', false)
674 headerButtonPreviousReset()
675 })
676
677 // * Responsive
678 let cWidth = inject('containerWidth')
679 let dWidth = inject('dialogWidth')
680 let responsiveClass = computed(() => {
681 return useResponsiveClass(props.inDialog ? dWidth.value : cWidth.value)
682 })
683
684 // * Fonts
685 let amFonts = inject('amFonts')
686
687 // * Colors
688 let amColors = inject('amColors')
689
690 // * Css Vars
691 let cssVars = computed(() => {
692 return {
693 '--am-font-family': amFonts.value.fontFamily,
694 '--am-c-eli-primary': amColors.value.colorPrimary,
695 '--am-c-eli-bgr': amColors.value.colorMainBgr,
696 '--am-c-eli-text': amColors.value.colorMainText,
697 '--am-c-eli-text-op90': useColorTransparency(amColors.value.colorMainText, 0.9),
698 '--am-c-eli-text-op80': useColorTransparency(amColors.value.colorMainText, 0.8),
699 '--am-c-eli-text-op70': useColorTransparency(amColors.value.colorMainText, 0.7),
700 '--am-c-eli-text-op60': useColorTransparency(amColors.value.colorMainText, 0.7),
701 '--am-c-eli-text-op20': useColorTransparency(amColors.value.colorMainText, 0.1),
702 }
703 })
704 </script>
705
706 <script>
707 export default {
708 name: 'EventInfo',
709 key: 'info',
710 label: 'event_info',
711 }
712 </script>
713
714 <style lang="scss">
715 .amelia-v2-booking #amelia-container {
716 // eli - event list info
717 .am-eli {
718 * {
719 font-family: var(--am-font-family);
720 letter-spacing: normal;
721 word-break: break-word;
722 }
723
724 .am-eli {
725 &__image {
726 position: relative;
727 overflow: hidden;
728 margin: 8px 0 16px;
729 }
730
731 &__header {
732 display: flex;
733 flex-direction: row;
734
735 p {
736 margin-bottom: 0;
737 }
738 }
739
740 &__main {
741 padding-bottom: 16px;
742
743 &-item {
744 margin-bottom: 12px;
745 }
746
747 .el-tabs {
748 &__nav {
749 &-wrap {
750 overflow: visible;
751
752 &:after {
753 background-color: var(--am-c-eli-text-op20);
754 }
755 }
756
757 &-scroll {
758 overflow: visible;
759 }
760 }
761
762 &__item {
763 display: inline-flex;
764 align-items: center;
765 justify-content: center;
766 margin-right: 16px;
767 font-size: 14px;
768 font-weight: 500;
769 line-height: 1.42857;
770 color: var(--am-c-eli-text-op90);
771
772 &:focus {
773 box-shadow: none;
774 }
775
776 &.is-active {
777 color: var(--am-c-eli-primary);
778 }
779
780 &:focus-visible {
781 box-shadow: 0 0 0 2px var(--am-c-eli-primary);
782 }
783 }
784
785 &__active-bar {
786 background-color: var(--am-c-eli-primary);
787 }
788
789 &__content {
790 padding: 12px 0;
791 }
792 }
793 }
794
795 &__timetable {
796 &-list {
797 list-style: none;
798 margin: 0;
799 padding: 0;
800 }
801
802 &-title {
803 font-size: 14px;
804 font-weight: 500;
805 line-height: 1.42857;
806 color: var(--am-c-eli-text-op70);
807 margin: 0 0 8px;
808 word-break: break-word;
809 }
810
811 &-range {
812 font-size: 14px;
813 font-weight: 500;
814 line-height: 1.42857;
815 color: var(--am-c-eli-text-op80);
816 border-bottom: 1px solid var(--am-c-eli-text-op20);
817 margin: 0 0 8px;
818 }
819
820 &-main {
821 margin: 0 0 2px;
822
823 & > * {
824 font-size: 14px;
825 line-height: 1.42857;
826 color: var(--am-c-eli-text-op80);
827 }
828
829 &__date {
830 font-weight: 500;
831 }
832
833 &__time {
834 font-weight: 400;
835 }
836 }
837 }
838 }
839
840 &__tickets-list {
841 list-style: none;
842 margin: 0;
843 padding: 0;
844 }
845
846 &__bringing {
847 &-footer {
848 display: flex;
849 align-items: center;
850 justify-content: flex-end;
851 width: 100%;
852 gap: 8px;
853
854 &.am-rw-500 {
855 flex-direction: column;
856
857 .am-button--secondary {
858 order: 2;
859 width: 100%;
860 }
861
862 .am-button--primary {
863 order: 1;
864 width: 100%;
865 }
866 }
867 }
868 }
869 }
870 }
871 </style>
872