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 / common / EventListFormConstruction / Header / Header.vue
ameliabooking / v3 / src / views / common / EventListFormConstruction / Header Last commit date
Header.vue 5 days ago
Header.vue
258 lines
1 <template>
2 <div
3 v-if="props.ready && !props.loading && !props.loadingUpcoming"
4 class="am-el__header-inner"
5 role="region"
6 :aria-label="displayLabels(stepsArray[stepIndex].label)"
7 :aria-busy="props.loading || props.loadingUpcoming"
8 :style="cssVars"
9 >
10 <AmButton
11 v-if="stepIndex < stepsArray.length - 1"
12 ref="backButtonRef"
13 class="am-heading-prev"
14 :icon="IconArrowLeft"
15 :icon-only="true"
16 size="micro"
17 type="plain"
18 category="secondary"
19 :aria-label="amLabels.previous_step || 'Previous step'"
20 :disabled="props.loading"
21 @click="previousStep"
22 ></AmButton>
23 <span class="am-el__header-inner__title" aria-live="polite" aria-atomic="true">
24 {{ displayLabels(stepsArray[stepIndex].label) }}
25 </span>
26 </div>
27 <span
28 v-else
29 class="am-el__skeleton"
30 role="status"
31 aria-live="polite"
32 :aria-label="amLabels.loading_header || 'Loading header'"
33 :style="cssVars"
34 >
35 <el-skeleton animated>
36 <template #template>
37 <el-skeleton-item />
38 </template>
39 </el-skeleton>
40 </span>
41 </template>
42
43 <script setup>
44 import AmButton from '../../../_components/button/AmButton.vue'
45 import IconArrowLeft from '../../../_components/icons/IconArrowLeft'
46
47 // * Import from Vue
48 import { ref, computed, inject, watch, nextTick, onBeforeUnmount } from 'vue'
49
50 // * Composables
51 import { useColorTransparency } from '../../../../assets/js/common/colorManipulation'
52
53 let props = defineProps({
54 customizedLabels: {
55 type: Object,
56 default: () => {
57 return {}
58 },
59 },
60 ready: {
61 type: Boolean,
62 default: false,
63 },
64 loading: {
65 type: Boolean,
66 default: false,
67 },
68 loadingUpcoming: {
69 type: Boolean,
70 default: false,
71 },
72 })
73
74 // * Popup steps array
75 let stepsArray = inject('stepsArray')
76
77 // * Step Index
78 const stepIndex = inject('stepIndex')
79 const backButtonRef = ref(null)
80 const focusFallbackIntervalRef = ref(null)
81
82 function clearFocusFallbackInterval() {
83 if (focusFallbackIntervalRef.value) {
84 clearInterval(focusFallbackIntervalRef.value)
85 focusFallbackIntervalRef.value = null
86 }
87 }
88
89 function tryFocusBackButton() {
90 const backButtonElement = backButtonRef.value?.$el || backButtonRef.value
91 const focusTarget =
92 backButtonElement && typeof backButtonElement.querySelector === 'function'
93 ? backButtonElement.querySelector('button') || backButtonElement
94 : backButtonElement
95
96 if (focusTarget && typeof focusTarget.focus === 'function') {
97 focusTarget.focus()
98 return true
99 }
100
101 return false
102 }
103
104 // * Step Functions
105 const { previousStep } = inject('changingStepsFunctions', {
106 previousStep: () => {},
107 })
108
109 // * Non modified labels
110 let amLabels = inject('labels')
111
112 function displayLabels(label) {
113 return Object.keys(props.customizedLabels).length && props.customizedLabels[label]
114 ? props.customizedLabels[label]
115 : amLabels[label]
116 }
117
118 // * Colors
119 let amColors = inject(
120 'amColors',
121 ref({
122 colorPrimary: '#1246D6',
123 colorSuccess: '#019719',
124 colorError: '#B4190F',
125 colorWarning: '#CCA20C',
126 colorMainBgr: '#FFFFFF',
127 colorMainHeadingText: '#33434C',
128 colorMainText: '#1A2C37',
129 colorSbBgr: '#17295A',
130 colorSbText: '#FFFFFF',
131 colorInpBgr: '#FFFFFF',
132 colorInpBorder: '#D1D5D7',
133 colorInpText: '#1A2C37',
134 colorInpPlaceHolder: '#1A2C37',
135 colorDropBgr: '#FFFFFF',
136 colorDropBorder: '#D1D5D7',
137 colorDropText: '#0E1920',
138 colorBtnPrim: '#265CF2',
139 colorBtnPrimText: '#FFFFFF',
140 colorBtnSec: '#1A2C37',
141 colorBtnSecText: '#FFFFFF',
142 }),
143 )
144
145 // * Css Vars
146 let cssVars = computed(() => {
147 return {
148 '--am-c-el-text-op15': useColorTransparency(amColors.value.colorMainText, 0.15),
149 }
150 })
151
152 watch(
153 () => stepIndex.value,
154 async () => {
155 clearFocusFallbackInterval()
156
157 await nextTick()
158
159 if (tryFocusBackButton()) {
160 return
161 }
162
163 focusFallbackIntervalRef.value = setInterval(() => {
164 if (tryFocusBackButton() || (!props.loading && props.ready && !props.loadingUpcoming)) {
165 clearFocusFallbackInterval()
166 }
167 }, 250)
168 },
169 {
170 flush: 'post',
171 immediate: true,
172 },
173 )
174
175 onBeforeUnmount(() => {
176 clearFocusFallbackInterval()
177 })
178 </script>
179
180 <script>
181 export default {
182 name: 'EventListHeader',
183 }
184 </script>
185
186 <style lang="scss">
187 @mixin am-dialog-event-list-header {
188 // el - event list
189 .am-el {
190 &__header-inner {
191 height: 60px;
192 padding: 18px 24px;
193 box-shadow: 0 2px 3px var(--am-c-el-text-op15);
194
195 &__title {
196 font-family: var(--am-font-family);
197 font-size: 18px;
198 font-weight: 500;
199 font-style: normal;
200 line-height: 1.55;
201 text-transform: initial;
202 letter-spacing: initial;
203 color: var(--am-c-main-heading-text);
204 margin: 0;
205 white-space: nowrap;
206
207 &:before {
208 // theme Twenty Nineteen
209 display: none;
210 }
211 }
212
213 .am-heading {
214 &-prev {
215 margin-right: 12px;
216 box-shadow: none;
217 }
218
219 &-next {
220 margin-left: 12px;
221 box-shadow: none;
222 }
223 }
224 }
225
226 &__skeleton {
227 display: flex;
228 flex-direction: row;
229 align-items: center;
230 padding: 18px 24px;
231 box-shadow: 0 2px 3px var(--am-c-el-text-op15);
232
233 .el-skeleton {
234 padding: 0;
235 &__item {
236 width: 170px;
237 height: 28px;
238 }
239 }
240 }
241
242 &-mobile {
243 padding: 16px;
244 }
245 }
246 }
247
248 // Public
249 .amelia-v2-booking #amelia-container {
250 @include am-dialog-event-list-header;
251 }
252
253 // Public
254 #amelia-app-backend-new #amelia-container {
255 @include am-dialog-event-list-header;
256 }
257 </style>
258