feat: improve teaser popup/modal by integrating with htmx

This commit is contained in:
Björn Fromme
2024-11-14 15:45:23 +01:00
parent 09aaf4f9f5
commit f79096c11a
24 changed files with 105 additions and 89 deletions
@@ -0,0 +1,17 @@
<?php
namespace EP\EpProducts\Controller;
use EP\EpProducts\Domain\Model\Concept;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class AjaxTeaserPopupController extends ActionController
{
public function conceptAction(Concept $concept, string $targetUrl)
{
$this->view->assignMultiple([
'concept' => $concept,
'targetUrl' => $targetUrl,
]);
}
}
@@ -135,8 +135,8 @@ class ProductRepository extends AbstractRepository
'pseudo_price as pseudoPrice', 'nights as nights', 'available as available', 'season as season',
'skipass_included as skipassIncluded', 'bus_included as busIncluded',
'earlybird', 'new', 'daytrip', 'hide_booking_button as hideBookingButton',
'low_contingent as lowContingent', 'concept_code as conceptCode', 'concept_name as conceptName',
'date.concept_popup_text as conceptPopupText', 'board', 'new'
'low_contingent as lowContingent', 'concept as conceptUid', 'concept_code as conceptCode',
'concept_name as conceptName', 'concept_popup_text as conceptPopupText', 'board', 'new'
)
->from('tx_epproducts_domain_model_date', 'date')
->where('date_start >= NOW()')
@@ -203,6 +203,7 @@ class ProductDataService implements SingletonInterface
'images' => $this->productImageService->getImages($teaserData['productUid']),
],
'concept' => [
'uid' => $teaserData['conceptUid'],
'name' => $teaserData['conceptName'],
'code' => $teaserData['conceptCode'],
'popupText' => $teaserData['conceptPopupText'],
@@ -225,6 +225,7 @@ return [
'cols' => 40,
'rows' => 5,
'eval' => 'trim',
'enableRichtext' => true,
],
],
'teaser_images' => [
@@ -181,6 +181,9 @@ tx_epproducts_ajax_json {
AjaxGroupsPrice {
1 = processForm
}
AjaxTeaserPopup {
1 = concept
}
}
features.requireCHashArgumentForActionArguments = 0
settings =< plugin.tx_epproducts.settings
@@ -268,6 +268,7 @@ $boot = function () {
'AjaxCalendar' => 'range,contingents,availableRooms',
'AjaxWatchlist' => 'list',
'AjaxGroupsPrice' => 'processForm',
'AjaxTeaserPopup' => 'concept',
],
[
'AjaxSearch' => 'searchresult',
@@ -278,6 +279,7 @@ $boot = function () {
'AjaxCalendar' => 'range,contingents,availableRooms',
'AjaxWatchlist' => 'list',
'AjaxGroupsPrice' => 'processForm',
'AjaxTeaserPopup' => 'concept',
]
);
@@ -44,3 +44,8 @@ import { definitionsFromContext } from '@hotwired/stimulus-webpack-helpers'
const application = Application.start()
const context = require.context('./controllers', true, /\.js$/)
application.load(definitionsFromContext(context))
window.htmx = require('htmx.org')
htmx.config.includeIndicatorStyles = false
htmx.config.historyEnabled = false
htmx.config.allowScriptTags = false
@@ -15,4 +15,8 @@ export default class extends Controller {
close() {
this.element.classList.add('hidden', true)
}
remove() {
this.element.remove()
}
}
@@ -1,24 +0,0 @@
import { Controller } from '@hotwired/stimulus'
export default class extends Controller {
static outlets = ['teaser-popup-modal']
static values = {
title: {
type: String,
default: 'Hinweis',
},
text: String,
}
open(e) {
if (this.hasTextValue) {
this.teaserPopupModalOutlet.titleTarget.innerText = this.titleValue
this.teaserPopupModalOutlet.contentTarget.innerHTML = this.textValue
this.teaserPopupModalOutlet.buttonTarget.href = this.element.href
this.teaserPopupModalOutlet.open()
}
e.preventDefault()
}
}
@@ -1,14 +0,0 @@
import { Controller } from '@hotwired/stimulus'
export default class extends Controller {
static targets = ['title', 'content', 'button']
open() {
this.element.classList.remove('hidden')
}
close() {
this.element.classList.add('hidden')
}
}
@@ -13,6 +13,5 @@
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
<f:render partial="TeaserPopup" arguments="{_all}" />
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -19,6 +19,5 @@
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
</f:else>
</f:if>
<f:render partial="TeaserPopup" arguments="{_all}" />
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -10,6 +10,5 @@
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="TeaserPopup" arguments="{_all}" />
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -8,6 +8,5 @@
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="TeaserPopup" arguments="{_all}" />
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -21,6 +21,5 @@
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
</f:else>
</f:if>
<f:render partial="TeaserPopup" arguments="{_all}" />
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -25,6 +25,5 @@
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
<f:render partial="TeaserPopup" arguments="{_all}" />
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -19,6 +19,5 @@
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="TeaserPopup" arguments="{_all}" />
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -25,6 +25,5 @@
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
<f:render partial="TeaserPopup" arguments="{_all}" />
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -19,6 +19,5 @@
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="TeaserPopup" arguments="{_all}" />
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -5,6 +5,7 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:section name="Teaser">
<f:variable name="targetUrl" value="{ep:uri.product(forceHotelUid: forceHotelUid, product: product, hotel: hotel, dateFrom: dateFrom, dateTo: dateTo, ref: referringPageUri)}"/>
<div class="product-teaser flex flex-col h-full bg-white group{f:if(condition: landscape, then: ' sm:flex-row sm:h-auto')}">
<f:render section="TeaserImage" arguments="{_all}"/>
<f:render section="TeaserContent" arguments="{_all}"/>
@@ -13,12 +14,11 @@
<f:section name="TeaserImage">
<div class="relative{f:if(condition: landscape, then: ' sm:w-2/5')}">
<a href="{ep:uri.product(forceHotelUid: forceHotelUid, product: product, hotel: hotel, dateFrom: dateFrom, dateTo: dateTo, ref: referringPageUri)}"
<a href="{targetUrl}"
<f:if condition="{concept.popupText} && {settings.globalConceptCode} != {concept.code}">
data-controller="teaser-popup"
data-teaser-popup-text-value="{concept.popupText}"
data-teaser-popup-teaser-popup-modal-outlet="#teaser-popup-modal"
data-action="teaser-popup#open"
hx-get="{ep:uri.ajax(action: 'concept', controller: 'AjaxTeaserPopup', arguments: '{concept: concept.uid, targetUrl: targetUrl}', format: 'html', pageUid: settings.defaultAjaxUid)}"
hx-target="body"
hx-swap="beforeend"
</f:if>
title="{region.name} {product.name} Details und Buchen">
<f:if condition="{productImage}">
@@ -78,12 +78,11 @@
<f:section name="TeaserContent">
{f:variable(name: 'including', value: '{f:render(section: \'Including\', arguments: \'{_all}\') -> v:format.trim()}')}
<a href="{ep:uri.product(forceHotelUid: forceHotelUid, product: product, hotel: hotel, dateFrom: dateFrom, dateTo: dateTo, ref: referringPageUri)}"
<a href="{targetUrl}"
<f:if condition="{concept.popupText} && {globalConceptCode} != {concept.code}">
data-controller="teaser-popup"
data-teaser-popup-text-value="{concept.popupText}"
data-teaser-popup-teaser-popup-modal-outlet="#teaser-popup-modal"
data-action="teaser-popup#open"
hx-get="{ep:uri.ajax(action: 'concept', controller: 'AjaxTeaserPopup', arguments: '{concept: concept.uid, targetUrl: targetUrl}', format: 'html', pageUid: settings.defaultAjaxUid)}"
hx-target="body"
hx-swap="beforeend"
</f:if>
title="{region.name} {product.name} Details und Buchen"
class="flex-1 flex flex-col space-y-1 px-2 py-4 md:p-4">
@@ -1,24 +0,0 @@
<div class="hidden fixed inset-0 w-full h-full z-50"
id="teaser-popup-modal"
data-controller="teaser-popup-modal">
<div class="absolute inset-0 w-full h-full bg-black/85 z-25"
data-action="click->teaser-popup-modal#close"></div>
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 w-full max-w-xl px-4">
<div class="shadow flex items-center justify-between p-4 bg-ep-primary-light text-white rounded-t">
<span class="text-xl font-bold" data-teaser-popup-modal-target="title"></span>
<button type="button"
class="inline-block"
data-action="teaser-popup-modal#close">
<svg class="w-8 h-8">
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-close"></use>
</svg>
</button>
</div>
<div class="p-4 bg-white rounded-b" data-rte-content>
<p data-teaser-popup-modal-target="content"></p>
<a href="#" class="button bg-button" data-teaser-popup-modal-target="button">
Ok
</a>
</div>
</div>
</div>
@@ -0,0 +1,43 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
data-namespace-typo3-fluid="true">
<f:layout name="Default"/>
<f:section name="Main">
<div class="fixed inset-0 w-full h-full z-50 {concept.code}" data-controller="modal">
<div class="absolute inset-0 w-full h-full bg-black/75 z-25"
data-action="click->modal#remove"></div>
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 w-full max-w-screen-lg px-4">
<div class="shadow flex items-center justify-between p-4 bg-concept-{concept.code} text-white rounded-t">
<span class="text-xl font-bold">
Hinweis
</span>
<button type="button"
class="inline-block"
data-action="modal#remove">
<svg class="w-8 h-8">
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-close"></use>
</svg>
</button>
</div>
<div class="p-4 bg-white rounded-b">
<div class="grid md:grid-cols-3 gap-8">
<f:image image="{concept.teaserImages.0}" width="640" class="block w-full h-auto" alt="{concept.name}"/>
<div class="md:col-span-2" data-rte-content>
<h3>
{concept.name}
</h3>
{concept.popupText -> f:format.html()}
<a href="{targetUrl}" class="button bg-button">
Ok
</a>
</div>
</div>
</div>
</div>
</div>
</f:section>
</html>