Drop stimulus, migrate to alpinejs

This commit is contained in:
Björn Fromme
2021-07-27 14:06:12 +02:00
committed by Björn Fromme
parent 74fa9ee952
commit 6b59f5a699
48 changed files with 456 additions and 514 deletions
@@ -3,12 +3,13 @@ require('cookieconsent')
require('lazysizes')
require('lazysizes/plugins/unveilhooks/ls.unveilhooks')
require('picturefill')
require('leaflet')
import Glightbox from 'glightbox'
// Polyfills
import smoothscroll from 'smoothscroll-polyfill'
smoothscroll.polyfill()
// Alpine stuff
import Alpine from 'alpinejs'
import parseAppConfig from './components/appconfig'
import initSliders from './components/sliders'
@@ -16,6 +17,10 @@ import appendPaCode from './components/pacode'
import scrollTop from './components/scoll-top'
import sticky from './components/sticky'
import watchlistToggle from './components/watchlist-toggle'
import watchlistButton from './components/watchlist-button'
import collapse from './components/collapse'
import fbpixel from './components/fbpixel'
import ajaxContent from './components/ajax-content'
// Executed on document ready
document.addEventListener('DOMContentLoaded', () => {
@@ -32,7 +37,16 @@ document.addEventListener('DOMContentLoaded', () => {
Alpine.data('scrollTop', scrollTop)
Alpine.data('sticky', sticky)
Alpine.data('watchlistToggle', watchlistToggle)
Alpine.data('watchlistButton', watchlistButton)
Alpine.data('collapse', collapse)
Alpine.data('fbpixel', fbpixel)
Alpine.data('ajaxContent', ajaxContent)
Alpine.start()
Glightbox({
selector: 'data-glightbox',
slideEffect: 'fade',
})
})
@@ -43,7 +57,6 @@ import Vue from 'vue'
import $ from 'jquery'
import dayjs from 'dayjs'
import 'dayjs/locale/de'
import '@fancyapps/fancybox'
global.$ = global.jQuery = $;
@@ -53,23 +66,15 @@ require('bootstrap-sass/assets/javascripts/bootstrap/modal')
require('bootstrap-sass/assets/javascripts/bootstrap/transition')
// Import components
import EventBus from './_bus'
import SearchBar from './components/SearchBar.vue'
import SearchBox from './components/SearchBox.vue'
import ProductDetail from './components/ProductDetail.vue'
import OsmMap from './components/OsmMap.vue'
import Calendar from './components/Calendar.vue'
import EventPricetable from './components/EventPriceTable.vue'
import ContactForm from './components/ContactForm.vue'
import Watchlist from './components/Watchlist.vue'
import HotelList from './components/HotelList.vue'
// Define global constants
const $window = $(window);
// Configure fancybox
$.fancybox.defaults.iframe.css.width = '800px';
// Define date filter for vue templates
Vue.filter('date', (value) => {
return dayjs(value).format('DD.MM.YY');
@@ -87,7 +92,7 @@ new Vue({
SearchBox,
SearchResult: () => import('./components/SearchResult.vue'),
ProductDetail,
OsmMap,
OsmMap: () => import('./components/OsmMap.vue'),
Calendar,
EventPricetable,
ContactForm,
@@ -99,76 +104,10 @@ new Vue({
data () {
return {
maxWindowWidth: 991,
searchBoxVisible: false,
searchbarFixed: false,
mobileMode: false,
backgroundImageMode: 'mobile'
}
},
methods: {
setMobileMode () {
this.mobileMode = $window.outerWidth() < this.maxWindowWidth;
},
scrollTop () {
this.scroll(0);
},
scrollTo (anchorId) {
document.getElementById(anchorId).scrollIntoView({ behavior: 'smooth' })
},
scroll (top) {
window.scrollTo({ top: top, behavior: 'smooth' })
},
toggleSearchbox () {
this.searchBoxVisible = !this.searchBoxVisible;
},
toggleMobileNavigation () {
$('[data-mobile-menu]').collapse('toggle');
},
toggleAreaNavigation () {
const $nav = $('.area-nav');
if ($nav.hasClass('open')) {
$nav.removeClass('open').find('ul').slideUp('fast');
} else {
$nav.addClass('open').find('ul').slideDown('fast');
}
},
initDropdownMenuBehavior () {
const $dropdownLinks = $('.navbar-main').find('a.dropdown-toggle');
if (this.mobileMode) {
$dropdownLinks.each(function () {
$(this).attr('data-toggle', 'dropdown');
});
} else {
$dropdownLinks.each(function () {
$(this).removeAttr('data-toggle');
});
}
},
initListeners () {
$window.on('resize', () => {
this.setMobileMode();
this.initDropdownMenuBehavior();
});
$(document).on('click', event => {
const $clickedElement = $(event.target);
const isNavExpanded = $('#ep-navbar-burger-menu').hasClass('collapse in');
if (isNavExpanded && !$clickedElement.hasClass('navbar-toggle')) {
this.toggleMobileNavigation();
}
});
},
},
created () {
this.$store.dispatch('init');
EventBus.$on('scrollTo', (anchorId, offset) => {
document.getElementById(anchorId).scrollIntoView({ behavior: 'smooth' })
});
},
mounted () {
Vue.nextTick(() => {
this.setMobileMode();
this.initListeners();
this.initDropdownMenuBehavior();
});
}
});
@@ -1,7 +1,5 @@
'use strict';
require('font-awesome/css/font-awesome.min.css');
require('cookieconsent/build/cookieconsent.min.css');
require('@fancyapps/fancybox/dist/jquery.fancybox.min.css');
require('leaflet/dist/leaflet.css');
require('flatpickr/dist/flatpickr.css');
require('font-awesome/css/font-awesome.min.css')
require('cookieconsent/build/cookieconsent.min.css')
require('glightbox/dist/css/glightbox.css')
require('leaflet/dist/leaflet.css')
require('flatpickr/dist/flatpickr.css')
@@ -16,9 +16,9 @@
</div>
</div>
<div ref="table">
<div class="ajax-content-wrapper" data-controller="ajax-content" :data-ajax-content-uri-value="uri">
<div class="ajax-content" data-ajax-content-target="container"></div>
<div data-ajax-content-target="trigger">
<div class="ajax-content-wrapper" x-data="ajaxContent" :data-uri="uri">
<div class="ajax-content" x-ref="container"></div>
<div x-ref="trigger">
<img :src="loaderUri" alt="Loading" title="Loading">
</div>
</div>
@@ -43,7 +43,6 @@
loading: false,
activeSection: null,
singleDate: false,
referringPage: null
}
},
props: {
@@ -195,10 +194,6 @@
}
},
created () {
if (sessionStorage.getItem('referringPage')) {
this.referringPage = JSON.parse(sessionStorage.getItem('referringPage'));
sessionStorage.removeItem('referringPage');
}
EventBus.$on('loadDatesTable', () => {
this.loadDatesView();
});
@@ -61,9 +61,9 @@
</a>
</div>
</div>
<a href="#" x-data="watchlistToggle" :data-uid="item.key" x-on:click.prevent="toggle()">
<span data-microtip-position="top" x-bind:aria-label="label()" role="tooltip">
<i class="fa fa-plus-circle" x-bind:class="icon()"></i>
<a href="#" x-data="watchlistToggle" x-bind="trigger" :data-uid="item.key">
<span data-microtip-position="top" x-bind:aria-label="label" role="tooltip">
<i class="fa fa-plus-circle" x-bind:class="icon"></i>
</span>
</a>
</div>
@@ -0,0 +1,42 @@
import axios from 'axios'
import { debounce } from './throttle-debounce'
export default () => ({
uri: '',
loaded: false,
loading: false,
init() {
this.uri = this.$el.dataset.uri
window.addEventListener('scroll', debounce(() => {
this.load()
}, 250))
},
load() {
if (true === this.loaded || false === this.inViewport()) {
return
}
this.loading = true
axios({
url: this.uri,
responseType: 'text'
}).then((response) => {
this.loaded = true
this.$refs.container.innerHTML = response.data
this.$refs.trigger.remove()
}).catch(() => {
this.loaded = false
}).finally(() => {
this.loading = false
})
},
inViewport () {
const rect = this.$refs.trigger.getBoundingClientRect()
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
)
}
})
@@ -1,9 +1,7 @@
const parseAppConfig = () => {
export default () => {
const configElement = document.getElementById('appconfig');
if (null === configElement) {
return {}
}
return JSON.parse(configElement.innerHTML);
}
export default parseAppConfig
@@ -0,0 +1,35 @@
import { debounce } from './throttle-debounce'
export default () => ({
collapsed: false,
init() {
const initialState = this.$el.dataset.collapsed
if (initialState !== undefined) {
this.collapsed = true
}
this.checkBreakpoints()
window.addEventListener('resize', debounce(() => {
this.checkBreakpoints()
}, 250))
},
checkBreakpoints() {
const until = this.$el.dataset.collapseUntil
if (until !== undefined) {
this.collapsed = window.innerWidth < parseInt(until)
}
const from = this.$el.dataset.collapseFrom
if (from !== undefined) {
this.collapsed = window.innerWidth > parseInt(from)
}
},
trigger: {
['@click.prevent']() {
this.collapsed = !this.collapsed
}
},
container: {
['x-show']() {
return this.collapsed === false
},
}
})
@@ -1,7 +0,0 @@
export default (callback, interval) => {
let debounceId
return (...args) => {
clearTimeout(debounceId)
debounceId = setTimeout(() => callback.apply(this, args), interval)
}
}
@@ -0,0 +1,6 @@
export default () => ({
init() {
const props = JSON.parse(this.$el.dataset.fbpixelProps)
fbq('track', 'ViewContent', props)
}
})
@@ -1,4 +1,4 @@
const appendPaCode = appConfig => {
export default appConfig => {
const paCode = appConfig.paCode
const externalDomains = appConfig.externalDomains
if (!paCode) {
@@ -23,5 +23,3 @@ const appendPaCode = appConfig => {
element.href = url
})
}
export default appendPaCode
@@ -1,15 +1,18 @@
import debounce from './debounce'
import { debounce } from './throttle-debounce'
export default function () {
return {
show: false,
init() {
window.addEventListener('scroll', debounce(() => {
this.show = window.scrollY > window.innerHeight/3
}, 250))
export default () => ({
show: false,
trigger: {
['@click.prevent']() {
window.scroll({top: 0, left: 0, behavior: 'smooth'})
},
scroll() {
window.scroll({ top: 0, left: 0, behavior: 'smooth' })
['x-show']() {
return this.show
}
}
}
},
init() {
window.addEventListener('scroll', debounce(() => {
this.show = window.scrollY > window.innerHeight / 3
}, 250))
},
})
@@ -96,7 +96,7 @@ const presets = {
},
}
const initSliders = () => {
export default () => {
document.querySelectorAll( '[data-slider]').forEach(element => {
let options = {}
const preset = element.dataset.slider
@@ -106,5 +106,3 @@ const initSliders = () => {
new Splide(element, options).mount()
})
}
export default initSliders
@@ -1,16 +1,14 @@
import debounce from './debounce'
import { debounce } from './throttle-debounce'
export default function () {
return {
sticky: false,
offset: 0,
init() {
if (this.$el.dataset.offset) {
this.offset = this.$el.dataset.offset
}
window.addEventListener('scroll', debounce(() => {
this.sticky = window.scrollY > this.offset
}, 250))
export default () => ({
sticky: false,
offset: 0,
init() {
if (this.$el.dataset.offset) {
this.offset = this.$el.dataset.offset
}
window.addEventListener('scroll', debounce(() => {
this.sticky = window.scrollY > this.offset
}, 250))
}
}
})
@@ -0,0 +1,19 @@
const debounce = (callback, interval) => {
let debounceId
return (...args) => {
clearTimeout(debounceId)
debounceId = setTimeout(() => callback.apply(this, args), interval)
}
}
const throttle = (callback, interval) => {
let enableCall = true
return (...args) => {
if (!enableCall) return
enableCall = false
callback.apply(this, args)
setTimeout(() => enableCall = true, interval)
}
}
export { debounce, throttle }
@@ -0,0 +1,8 @@
export default () => ({
show() {
return this.$store.watchlist.length > 0
},
label() {
return `Merkliste (${this.$store.watchlist.length})`
}
})
@@ -1,19 +1,7 @@
export default function () {
return {
uid: null,
init() {
this.uid = this.$el.dataset.uid
},
onList() {
return -1 !== this.$store.watchlist.indexOf(this.uid)
},
label() {
return this.onList() ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen'
},
icon() {
return this.onList() ? 'fa-minus-circle' : 'fa-plus-circle'
},
toggle() {
export default () => ({
uid: null,
trigger: {
['@click.prevent']() {
const watchlistIndex = this.$store.watchlist.indexOf(this.uid)
if (-1 !== watchlistIndex) {
this.$store.watchlist.splice(watchlistIndex, 1)
@@ -22,5 +10,17 @@ export default function () {
}
sessionStorage.setItem('myep-watchlist', JSON.stringify(this.$store.watchlist))
}
}
}
},
init() {
this.uid = this.$el.dataset.uid
},
onList() {
return -1 !== this.$store.watchlist.indexOf(this.uid)
},
icon() {
return this.onList() ? 'fa-minus-circle' : 'fa-plus-circle'
},
label() {
return this.onList() ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen'
},
})
@@ -1,57 +0,0 @@
import { Controller } from 'stimulus'
import { useDebounce } from '../mixins/useDebounce'
import axios from 'axios'
export default class extends Controller {
static targets = [ 'trigger', 'container' ]
static values = {
uri: String,
loaded: Boolean,
loading: Boolean
}
connect() {
useDebounce(this)
window.addEventListener('scroll', this.debounce(() => {
this.loadContent()
}, 250))
}
loadContent () {
if (true === this.loadedValue || false === this.isInViewport()) {
return
}
this.loadingValue = true
axios({
url: this.uriValue,
responseType: 'text'
}).then((response) => {
this.loadingValue = false
this.loadedValue = true
this.containerTarget.innerHTML = response.data
this.triggerTarget.remove()
}).catch(() => {
this.loadingValue = false
this.loadedValue = false
})
}
isInViewport () {
const rect = this.triggerTarget.getBoundingClientRect()
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
loadingValueChanged () {
if (false === this.loadedValue) {
this.triggerTarget.classList.toggle('loading', this.loadingValue)
}
}
}
@@ -1,27 +0,0 @@
import { Controller } from 'stimulus'
export default class extends Controller {
static classes = [ 'collapsed' ]
static targets = [ 'container' ]
static values = { collapsed: Boolean, until: Number, from: Number }
connect() {
if (this.hasUntilValue && window.innerWidth < this.untilValue) {
this.collapsedValue = true
}
if (this.hasFromValue && window.innerWidth > this.fromValue) {
this.collapsedValue = true
}
}
toggle(e) {
e.preventDefault()
this.collapsedValue = !this.collapsedValue
}
collapsedValueChanged() {
const collapsedClass = this.hasCollapsedClass ? this.collapsedClass : 'hidden'
this.containerTarget.classList.toggle(collapsedClass, this.collapsedValue)
}
}
@@ -1,30 +0,0 @@
import { Controller } from 'stimulus'
export default class extends Controller {
static targets = [ 'watchlist', 'watchlistLabel' ]
static values = { watchlist: Array }
connect() {
const initialList = JSON.parse(sessionStorage.getItem('myep-watchlist'))
this.watchlistValue = initialList ? initialList : []
}
toggleWatchlist(e) {
const uid = e.detail
const currentList = this.watchlistValue
const listIndex = currentList.indexOf(uid)
if (-1 === listIndex) {
currentList.push(uid)
} else {
currentList.splice(listIndex, 1)
}
this.watchlistValue = currentList
sessionStorage.setItem('myep-watchlist', JSON.stringify(currentList))
}
watchlistValueChanged() {
this.watchlistTarget.style.display = this.watchlistValue.length > 0 ? 'block' : 'none'
this.watchlistLabelTarget.textContent = `Merkliste (${this.watchlistValue.length})`
}
}
@@ -1,10 +0,0 @@
import { Controller } from 'stimulus'
export default class extends Controller {
static values = { props: Object }
connect() {
fbq('track', 'ViewContent', this.propsValue);
}
}
@@ -1,11 +0,0 @@
export const useDebounce = controller => {
Object.assign(controller, {
debounce: (callback, interval) => {
let debounceId
return (...args) => {
clearTimeout(debounceId)
debounceId = setTimeout(() => callback.apply(this, args), interval)
}
}
})
}
@@ -1,14 +0,0 @@
export const useThrottle = controller => {
Object.assign(controller, {
throttle: (callback, interval) => {
let enableCall = true
return (...args) => {
if (!enableCall) return
enableCall = false
callback.apply(this, args)
setTimeout(() => enableCall = true, interval)
}
}
})
}
@@ -6,7 +6,7 @@
[x-cloak] {
@apply hidden;
}
@import "variables";
@import "color";
@@ -22,7 +22,6 @@
@import "remix/breakpoints";
@import "remix/mixins";
@import "remix/base";
@import "remix/overrides";
@import "remix/badges";
@import "remix/buttons";
@import "remix/area-nav";
@@ -18,7 +18,6 @@ $output-bourbon-deprecation-warnings: false;
@import "remix/breakpoints";
@import "remix/mixins";
@import "remix/base";
@import "remix/overrides";
@import "remix/badges";
@import "remix/buttons";
@import "remix/area-nav";
@@ -18,7 +18,6 @@ $output-bourbon-deprecation-warnings: false;
@import "remix/breakpoints";
@import "remix/mixins";
@import "remix/base";
@import "remix/overrides";
@import "remix/badges";
@import "remix/buttons";
@import "remix/area-nav";
@@ -18,7 +18,6 @@ $output-bourbon-deprecation-warnings: false;
@import "remix/breakpoints";
@import "remix/mixins";
@import "remix/base";
@import "remix/overrides";
@import "remix/badges";
@import "remix/buttons";
@import "remix/area-nav";
@@ -17,7 +17,6 @@
}
.area-nav__list {
display: none;
position: absolute;
top: $area-nav-height;
left: 0;
@@ -1,6 +0,0 @@
.fancybox-navigation .fancybox-button {
@include mq($from: desktop) {
width: 105px;
height: 150px;
}
}
@@ -1,41 +1,46 @@
{namespace v=FluidTYPO3\Vhs\ViewHelpers}
<div class="ep-contactnav" data-controller="contactbar" data-action="watchlist-toggle@window->contactbar#toggleWatchlist">
<a href="{f:uri.typolink(parameter: settings.watchlistPageUid)}" class="bg-red watchlist"
data-contactbar-target="watchlist" rel="nofollow">
<span class="fa fa-list" aria-hidden="true"></span>
<div data-contactbar-target="watchlistLabel"></div>
</a>
<a href="tel:{settings.phoneNumberLink}" class="bg-red phone" title="Telefonische Beratung" rel="nofollow">
<span class="fa fa-phone" aria-hidden="true"></span>
<div>{settings.phoneNumber}</div>
</a>
<a href="https://api.whatsapp.com/send?phone={settings.whatsAppNumberLink}" target="_blank"
class="bg-red whatsapp" title="Whatsapp" rel="nofollow">
<span class="fa fa-whatsapp" aria-hidden="true"></span>
<div>{settings.whatsApp} als Kontakt speichern</div>
</a>
<a href="{settings.facebookUrl}" class="bg-yellow facebook" target="_blank">
<span class="fa fa-facebook" aria-hidden="true"></span>
<div>Facebook</div>
</a>
<a href="{settings.instagramUrl}" class="bg-yellow instagram" target="_blank">
<span class="fa fa-instagram" aria-hidden="true"></span>
<div>Instagram</div>
</a>
<a href="{settings.youtubeUrl}" class="bg-yellow youtube" target="_blank">
<span class="fa fa-youtube-play" aria-hidden="true"></span>
<div>Youtube</div>
</a>
<f:if condition="{settings.englishPageUid}">
<f:link.typolink parameter="{settings.englishPageUid}" title="English" class="bg-yellow english">
<span><f:image src="EXT:ep_theme/Resources/Public/images/gb.png" alt="English" class="ep-contactnav__icon"/></span>
<div>English</div>
</f:link.typolink>
</f:if>
<f:if condition="{settings.germanPageUid}">
<f:link.typolink parameter="{settings.germanPageUid}" title="English" class="bg-yellow german">
<span><f:image src="EXT:ep_theme/Resources/Public/images/de.png" alt="Deutsch" class="ep-contactnav__icon"/></span>
<div>Deutsch</div>
</f:link.typolink>
</f:if>
</div>
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="ep-contactnav">
<a href="{f:uri.typolink(parameter: settings.watchlistPageUid)}" class="bg-red watchlist"
x-data="watchlistButton" x-show="show" x-transition x-cloak rel="nofollow">
<span class="fa fa-list" aria-hidden="true"></span>
<div x-text="label"></div>
</a>
<a href="tel:{settings.phoneNumberLink}" class="bg-red phone" title="Telefonische Beratung" rel="nofollow">
<span class="fa fa-phone" aria-hidden="true"></span>
<div>{settings.phoneNumber}</div>
</a>
<a href="https://api.whatsapp.com/send?phone={settings.whatsAppNumberLink}" target="_blank"
class="bg-red whatsapp" title="Whatsapp" rel="nofollow">
<span class="fa fa-whatsapp" aria-hidden="true"></span>
<div>{settings.whatsApp} als Kontakt speichern</div>
</a>
<a href="{settings.facebookUrl}" class="bg-yellow facebook" target="_blank">
<span class="fa fa-facebook" aria-hidden="true"></span>
<div>Facebook</div>
</a>
<a href="{settings.instagramUrl}" class="bg-yellow instagram" target="_blank">
<span class="fa fa-instagram" aria-hidden="true"></span>
<div>Instagram</div>
</a>
<a href="{settings.youtubeUrl}" class="bg-yellow youtube" target="_blank">
<span class="fa fa-youtube-play" aria-hidden="true"></span>
<div>Youtube</div>
</a>
<f:if condition="{settings.englishPageUid}">
<f:link.typolink parameter="{settings.englishPageUid}" title="English" class="bg-yellow english">
<span><f:image src="EXT:ep_theme/Resources/Public/images/gb.png" alt="English" class="ep-contactnav__icon"/></span>
<div>English</div>
</f:link.typolink>
</f:if>
<f:if condition="{settings.germanPageUid}">
<f:link.typolink parameter="{settings.germanPageUid}" title="English" class="bg-yellow german">
<span><f:image src="EXT:ep_theme/Resources/Public/images/de.png" alt="Deutsch" class="ep-contactnav__icon"/></span>
<div>Deutsch</div>
</f:link.typolink>
</f:if>
</div>
</html>
@@ -9,7 +9,7 @@
</div>
<div class="slider__items">
<f:alias map="{image: images.0}">
<a href="javascript:;" data-fancybox-trigger="gallery-{id}">
<a href="javascript:;" data-glightbox="gallery-{id}">
<f:image image="{image}" width="770" height="430c+100" class="slider__image"
alt="{image.alternative}" title="{image.title}"/>
</a>
@@ -21,7 +21,7 @@
<f:for each="{images}" as="image" iteration="iteration">
<f:if condition="{iteration.cycle} <= 5">
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}"
data-fancybox="gallery-{id}" data-caption="{image.originalResource.description}" class="slider__thumb">
data-glightbox data-gallery="gallery-{id}" data-caption="{image.originalResource.description}" class="slider__thumb">
<f:image image="{image}" width="150" height="85c+100" class="img-responsive"
alt="{image.originalResource.alternative}" title="{image.originalResource.title}"/>
</a>
@@ -37,7 +37,7 @@
<f:then>
<f:alias map="{image: images.0}">
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}"
data-fancybox="gallery-{id}" data-caption="{image.originalResource.description}"
data-glightbox data-gallery="gallery-{id}" data-caption="{image.originalResource.description}"
title="{image.title}">1</a>
</f:alias>
</f:then>
@@ -45,7 +45,7 @@
<f:for each="{images}" as="image" iteration="iteration">
<f:if condition="{iteration.cycle} > 5">
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}"
data-fancybox="gallery-{id}" data-caption="{image.originalResource.description}"
data-glightbox data-gallery="gallery-{id}" data-caption="{image.originalResource.description}"
title="{image.originalResource.title}">{iteration.index}</a>
</f:if>
</f:for>
@@ -55,7 +55,7 @@
<f:else>
<f:for each="{images}" as="image">
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}"
data-fancybox="gallery-{id}" data-caption="{image.originalResource.description}"
data-glightbox data-gallery="gallery-{id}" data-caption="{image.originalResource.description}"
title="{image.originalResource.title}">{iteration.index}</a>
</f:for>
</f:else>
@@ -6,13 +6,13 @@
<div class="row accordion panel-group" id="ep-travel-accordion" role="tablist" aria-multiselectable="true">
<v:menu pageUid="{settings.footerNavPid}" levels="2" expandAll="true">
<f:for each="{menu}" as="menuheader" iteration="iteration">
<div class="col-sm-3" data-controller="collapse" data-collapse-until-value="991" data-collapse-collapsed-class="hidden">
<div class="col-sm-3" x-data="collapse" data-collapse-until="991">
<span class="headline headline--4 headline--footer hidden-xs">{menuheader.title}</span>
<a class="headline headline--4 headline--footer visible-xs" role="button" data-action="collapse#toggle" href="#">
<a class="headline headline--4 headline--footer visible-xs" role="button" x-bind="trigger" href="#">
{menuheader.title}
</a>
<v:menu pageUid="{menuheader.uid}" useShortcutUid="true" as="submenu">
<ul class="list-disc lg:block" data-collapse-target="container">
<ul class="list-disc lg:block" x-bind="container" x-transition>
<f:for each="{submenu}" as="submenuitem">
<f:if condition="{submenuitem.doktype} == 3">
<f:then>
@@ -90,6 +90,6 @@
</div>
</div>
</div>
<button x-data="scrollTop" x-on:click.prevent="scroll()" x-show="show" class="arrowtop " x-cloak>
<button x-data="scrollTop" x-bind="trigger" class="arrowtop" x-cloak>
<i class="fa fa-angle-up"></i>
</button>
@@ -117,9 +117,9 @@
</f:else>
</f:if>
</div>
<a href="#" class="teaserbox-watchlist" x-data="watchlistToggle" :data-uid="item.key" x-on:click.prevent="toggle()">
<span data-microtip-position="top" x-bind:aria-label="label()" role="tooltip">
<i class="fa fa-plus-circle" x-bind:class="icon()"></i>
<a href="#" class="teaserbox-watchlist" x-data="watchlistToggle" x-bind="trigger" :data-uid="item.key">
<span data-microtip-position="top" x-bind:aria-label="label" role="tooltip">
<i class="fa fa-plus-circle" x-bind:class="icon"></i>
</span>
</a>
</div>
@@ -5,7 +5,7 @@
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers" >
<f:section name="Mainnav">
<div class="navbar-main-wrapper" :class="{ 'searchbar-fixed': searchbarFixed }" x-data>
<div class="navbar-main-wrapper" x-data>
<nav class="container navbar navbar-default navbar-main{f:if(condition: data.tx_eptheme_hide_searchbar, then: ' navbar-main--no-searchbar')}">
<f:render section="NavbarHeader" arguments="{_all}"/>
<div class="hidden-xs hidden-sm">
@@ -3,7 +3,8 @@
{product.conceptDescription -> f:format.html()}
</div>
<f:if condition="{product.calendarHotel} && {hotel.groupsPriceConfigs}">
<a class="button button--full" data-fancybox data-type="iframe" href="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid)}">
<a class="button button--full" data-glightbox
href="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid)}">
Preisberechnung
</a>
</f:if>
@@ -115,9 +115,9 @@
</a>
</div>
</div>
<a href="#" x-data="watchlistToggle" data-uid="{item.key}" x-on:click.prevent="toggle()">
<span data-microtip-position="top" x-bind:aria-label="onList() ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen'" role="tooltip">
<i class="fa fa-plus-circle" x-bind:class="onList() ? 'fa-minus-circle' : 'fa-plus-circle'"></i>
<a href="#" x-data="watchlistToggle" x-bind="trigger" data-uid="{item.key}">
<span data-microtip-position="top" x-bind="label" role="tooltip">
<i class="fa fa-plus-circle" x-bind="icon"></i>
</span>
</a>
</div>
@@ -92,9 +92,9 @@
</a>
</div>
</div>
<a href="#" class="teaserbox-watchlist" x-data="watchlistToggle" :data-uid="item.key" x-on:click.prevent="toggle()">
<span data-microtip-position="top" x-bind:aria-label="label()" role="tooltip">
<i class="fa fa-plus-circle" x-bind:class="icon()"></i>
<a href="#" class="teaserbox-watchlist" x-data="watchlistToggle" x-bind="trigger" :data-uid="item.key">
<span data-microtip-position="top" x-bind:aria-label="label" role="tooltip">
<i class="fa fa-plus-circle" x-bind:class="icon"></i>
</span>
</a>
</div>
@@ -3,7 +3,7 @@
<p><strong>Webcam</strong></p>
<div>
<a href="{region.webcam.url}"
data-fancybox data-caption="Webcam {region.name}"
data-glightbox data-caption="Webcam {region.name}"
title="Webcam {region.name}">
<img src="{ep:uri.externalImage(url: region.webcam.url)}" width="330" class="img-responsive"/>
</a>
@@ -4,7 +4,7 @@
<div>
<f:for each="{region.regionMaps}" as="regionMap">
<a href="{f:uri.image(src: '{regionMap.uid}', treatIdAsReference: '1', maxWidth: '2560')}"
data-fancybox="regionmap-{region.uid}" data-caption="Pistenplan {region.name}"
data-glightbox data-gallery="regionmap-{region.uid}" data-caption="Pistenplan {region.name}"
title="Pistenplan {region.name}">
<img class="img-responsive lazyload"
data-src="{f:uri.image(image: regionMap, width: '330')}"
@@ -1,15 +1,15 @@
{namespace v=FluidTYPO3\Vhs\ViewHelpers}
{namespace ep=EP\EpTheme\ViewHelpers}
<div class="area-nav">
<div class="area-nav__inner">
<div class="area-nav__inner" x-data="{ open: false }">
<f:spaceless>
<f:if condition="{settings.hideAreaNavigation}">
<f:else>
<a class="area-nav__trigger area-nav__link" href="#" @click.prevent="toggleAreaNavigation()">
<a class="area-nav__trigger area-nav__link" href="#" x-on:click.prevent="open = !open">
Bereich wechseln <i class="area-nav__trigger-icon fa fa-lg fa-caret-down"></i>
</a>
<nav role="navigation">
<ul class="area-nav__list">
<ul class="area-nav__list" x-bind:class="open ? 'open' : ''" x-show="open" x-transition x-cloak>
<v:menu levels="1" useShortcutUid="1" pageUid="{settings.topNavPid}">
<f:for each="{menu}" as="menuitem">
<li class="area-nav__list-item">
@@ -24,10 +24,10 @@
<div class="col-xs-12">
<h2>Buchungsoptionen</h2>
<div class="ajax-content-wrapper"
data-controller="ajax-content"
data-ajax-content-uri-value="{ep:uri.ajax(action: 'pricetableHtml', controller: 'AjaxTable', pageUid: settings.defaultAjaxUid, arguments: '{product: product, hotel: hotel}', format: 'html')}">
<div class="ajax-content" data-ajax-content-target="container"></div>
<div data-ajax-content-target="trigger">
x-data="ajaxContent"
data-uri="{ep:uri.ajax(action: 'pricetableHtml', controller: 'AjaxTable', pageUid: settings.defaultAjaxUid, arguments: '{product: product, hotel: hotel}', format: 'html')}">
<div class="ajax-content" x-ref="container"></div>
<div x-ref="trigger">
<img src="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}" alt="Loading" title="Loading">
</div>
</div>
@@ -83,11 +83,11 @@
</div>
<div class="col-xs-12">
<p>
<a href="#" x-data="watchlistToggle" data-uid="{hotel.uid}:{product.uid}" x-on:click.prevent="toggle()">
<span data-microtip-position="top" x-bind:aria-label="label()" role="tooltip">
<i class="fa" x-bind:class="icon()"></i>
<a href="#" x-data="watchlistToggle" x-bind="trigger" data-uid="{hotel.uid}:{product.uid}">
<span data-microtip-position="top" x-bind:aria-label="label" role="tooltip">
<i class="fa" x-bind:class="icon"></i>
</span>
<span x-text="label()"></span>
<span x-text="label"></span>
</a>
</p>
<p>
@@ -260,14 +260,16 @@
</div>
</article>
</product-detail>
<div data-controller="facebook-pixel" data-facebook-pixel-props-value='{ "content_ids": [ {product.busProId -> f:format.raw()} ], "content_name": "{product.nameInternal -> f:format.raw()}", "content_type": "product" }'></div>
<div x-data="fbpixel" data-fbpixel-props='{ "content_ids": [ {product.busProId -> f:format.raw()} ], "content_name": "{product.nameInternal -> f:format.raw()}", "content_type": "product" }'></div>
<f:render partial="Faq" arguments="{faqs: product.faqs, settings: settings}"/>
</f:section>
<f:section name="Calendar">
<f:if condition="{product.calendarHotel}">
<f:if condition="{hotel.groupsPriceConfigs}">
<a class="button button--full" data-fancybox data-type="iframe" href="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid)}">
<a class="button button--full"
data-glightbox
href="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid)}">
Preisberechnung
</a>
</f:if>
@@ -47,7 +47,7 @@
</div>
</div>
</div>
<div data-controller="facebook-pixel" data-facebook-pixel-props-value='{ "content_ids": [ {region.uid -> f:format.raw()} ], "content_name": "{region.nameInternal -> f:format.raw()}", "content_type": "product" }'></div>
<div x-data="fbpixel" data-fbpixel-props='{ "content_ids": [ {region.uid -> f:format.raw()} ], "content_name": "{region.nameInternal -> f:format.raw()}", "content_type": "product" }'></div>
</f:section>
</html>
@@ -43,15 +43,15 @@
</f:section>
<f:section name="CollapsibleSimple">
<div class="panel panel-default" data-controller="collapse" data-collapse-collapsed-value="true">
<div class="panel panel-default" x-data="collapse" data-collapsed>
<div class="panel-heading">
<h4 class="panel-title">
<a role="button" href="#" data-action="collapse#toggle">
<a role="button" href="#" x-bind="trigger">
{label}
</a>
</h4>
</div>
<div class="hidden" data-collapse-target="container">
<div class="hidden" x-bind="container">
<div class="panel-body">
<f:if condition="{name}">
<div class="form-group">
@@ -77,15 +77,15 @@
</f:section>
<f:section name="CollapsibleHotel">
<div class="panel panel-default" data-controller="collapse" data-collapse-collapsed-value="true">
<div class="panel panel-default" x-data="collapse" data-collapsed>
<div class="panel-heading">
<h4 class="panel-title">
<a role="button" href="#" data-action="collapse#toggle">
<a role="button" href="#" data-bind="trigger">
{label}
</a>
</h4>
</div>
<div class="hidden" data-collapse-target="container">
<div class="hidden" data-bind="container">
<div class="panel-body">
<div class="form-group">
<label class="control-label">Name</label>
@@ -237,15 +237,15 @@
</f:section>
<f:section name="CollapsibleCity">
<div class="panel panel-default" data-controller="collapse" data-collapse-collapsed-value="true">
<div class="panel panel-default" x-data="collapse" data-collapsed>
<div class="panel-heading">
<h4 class="panel-title">
<a role="button" href="#" data-action="collapse#toggle">
<a role="button" href="#" x-bind="trigger">
{label}
</a>
</h4>
</div>
<div class="hidden" data-collapse-target="container">
<div class="hidden" x-bind="container">
<div class="panel-body">
<div class="col-xs-12">
<div class="form-group">
@@ -271,15 +271,15 @@
</f:section>
<f:section name="CollapsibleRegion">
<div class="panel panel-default" data-controller="collapse" data-collapse-collapsed-value="true">
<div class="panel panel-default" x-data="collapse" data-collapsed>
<div class="panel-heading">
<h4 class="panel-title">
<a role="button" href="#" data-action="collapse#toggle">
<a role="button" href="#" x-bind="trigger">
{label}
</a>
</h4>
</div>
<div class="hidden" data-collapse-target="container">
<div class="hidden" x-bind="container">
<div class="panel-body">
<div class="col-xs-12">
<div class="form-group">
@@ -333,15 +333,15 @@
</f:section>
<f:section name="CollapsibleImages">
<div class="panel panel-default" data-controller="collapse" data-collapse-collapsed-value="true">
<div class="panel panel-default" x-data="collapse" data-collapsed>
<div class="panel-heading">
<h4 class="panel-title">
<a role="button" href="#" data-action="collapse#toggle">
<a role="button" href="#" x-bind="trigger">
{label}
</a>
</h4>
</div>
<div class="hidden" data-collapse-target="containet">
<div class="hidden" x-bind="container">
<div class="panel-body">
<f:for each="{images}" as="image">
<a href="{image}" target="_blank">{image}</a><br>
@@ -5,7 +5,7 @@
<f:layout name="Default"/>
<f:section name="Main">
<div id="searchbox" class="searchbox hidden-lg" x-data x-show="$store.show.searchBox" x-cloak>
<div id="searchbox" class="searchbox hidden-lg" x-data x-show="$store.show.searchBox" x-transition x-cloak>
<search-box
options-uri='<ep:uri.ajax controller="AjaxSearchbar" action="options" pageUid="{settings.defaultAjaxUid}" />'
reset-uri='<ep:uri.ajax controller="AjaxSearchbar" action="reset" pageUid="{settings.defaultAjaxUid}" />'
@@ -21,13 +21,13 @@
</div>
<div class="container">
<div class="row">
<div class="col-md-3" data-controller="collapse" data-collapse-collapsed-class="hidden" data-collapse-until-value="991">
<div class="col-md-3" x-data="collapse" data-collapse-until="991">
<p class="lg:hidden">
<button class="button button--full button--small" role="button" data-action="collapse#toggle">
<button class="button button--full button--small" role="button" x-bind="trigger">
<i class="fa fa-toggle-on"></i> Filter ein-/ausblenden
</button>
</p>
<div id="filterpanel" class="hidden lg:block" v-cloak data-collapse-target="container">
<div id="filterpanel" class="lg:block" v-cloak x-bind="container" x-transition x-cloak>
<concept-buttons :concepts="filterOptions.concepts"></concept-buttons>
<h3>Filter</h3>
<a v-if="filterCount > 0"