diff --git a/bin/frontend/controls/SimpleCheckout.js b/bin/frontend/controls/SimpleCheckout.js index 25202ec5efbb4ac37e1e44562f7446f7cd4d2a9c..4d9a00c45bd83dac2bde185a33ce5b0ef6f87d32 100644 --- a/bin/frontend/controls/SimpleCheckout.js +++ b/bin/frontend/controls/SimpleCheckout.js @@ -10,11 +10,14 @@ define('package/quiqqer/order-simple-checkout/bin/frontend/controls/SimpleChecko 'qui/controls/Control', 'qui/controls/loader/Loader', 'qui/utils/Form', + 'Locale', 'Ajax' -], function (QUI, QUIControl, QUILoader, QUIFormUtils, QUIAjax) { +], function (QUI, QUIControl, QUILoader, QUIFormUtils, QUILocale, QUIAjax) { 'use strict'; + const lg = 'quiqqer/order-simple-checkout'; + return new Class({ Extends: QUIControl, @@ -23,7 +26,9 @@ define('package/quiqqer/order-simple-checkout/bin/frontend/controls/SimpleChecko Binds: [ 'update', '$onInject', - '$onImport' + '$onImport', + 'toggleAllProducts', + 'scrollToPayment' ], options: { @@ -41,6 +46,9 @@ define('package/quiqqer/order-simple-checkout/bin/frontend/controls/SimpleChecko this.$Payment = null; this.Loader = null; this.$PayToOrderBtn = null; + this.ScrollToPaymentBtn = null; + + this.showAllProductsBtn = null; this.addEvents({ onImport: this.$onImport, @@ -150,7 +158,6 @@ define('package/quiqqer/order-simple-checkout/bin/frontend/controls/SimpleChecko }); }); - this.Loader.hide(); moofx([ @@ -211,7 +218,6 @@ define('package/quiqqer/order-simple-checkout/bin/frontend/controls/SimpleChecko this.Loader.show(); }; - let SetCurrency = Promise.resolve(); if (typeof window.DEFAULT_USER_CURRENCY !== 'undefined' && @@ -297,7 +303,11 @@ define('package/quiqqer/order-simple-checkout/bin/frontend/controls/SimpleChecko this.$PayToOrderBtn = null; } - this.$setSpacingOnMobile(); + this.ScrollToPaymentBtn = this.getElm().querySelector('.quiqqer-simple-checkout__scrollToPaymentBtn'); + + if (this.ScrollToPaymentBtn) { + this.ScrollToPaymentBtn.addEvent('click', this.scrollToPayment); + } // load this.$Delivery.fireEvent('change'); @@ -398,7 +408,6 @@ define('package/quiqqer/order-simple-checkout/bin/frontend/controls/SimpleChecko QUI.parse(this.getElm()).then(() => { this.fireEvent('loaded', [this]); this.$onImport(); - this.$setSpacingOnMobile(); resolve(); }); @@ -557,6 +566,12 @@ define('package/quiqqer/order-simple-checkout/bin/frontend/controls/SimpleChecko QUIAjax.get('package_quiqqer_order-simple-checkout_ajax_frontend_basket', (basket) => { if (this.getElm().getElement('.quiqqer-simple-checkout-basket__inner')) { this.getElm().getElement('.quiqqer-simple-checkout-basket__inner').set('html', basket); + + this.showAllProductsBtn = this.getElm().querySelector('.articleList__btnShowMore'); + + if (this.showAllProductsBtn) { + this.showAllProductsBtn.addEvent('click', this.toggleAllProducts); + } } this.Loader.hide(); @@ -662,21 +677,81 @@ define('package/quiqqer/order-simple-checkout/bin/frontend/controls/SimpleChecko }, /** - * Calculate needed margin on mobile. - * Not pretty solution, needs to be reworked. + * Show or hide all products + * + * @param event */ - $setSpacingOnMobile: function () { - if (QUI.getBodySize().x >= 768) { + toggleAllProducts: function (event) { + event.stop(); + + const Elm = this.getElm(); + const HiddenList = Elm.querySelector('.articleList__hidden'), + InnerContainer = Elm.querySelector('.articleList__hiddenInner'); + + if (!HiddenList || !InnerContainer) { + return; + } + + if (HiddenList.offsetHeight > 0) { + this.hideHiddenArticles(HiddenList); + const max = this.showAllProductsBtn.getAttribute('data-qui-max'); + + this.showAllProductsBtn.innerHTML = QUILocale.get(lg, 'ordering.btn.showAllProductsText.open', { + max: max ? max : '' + }); + return; } - const PayContainer = this.getElm().querySelector('.quiqqer-simple-checkout-data-pay'); + this.showHiddenArticles(HiddenList, InnerContainer); + + this.showAllProductsBtn.innerHTML = QUILocale.get(lg, 'ordering.btn.showAllProductsText.close'); + }, + + /** + * Show hidden products + * + * @param ListNode + * @param InnerNode + */ + showHiddenArticles: function (ListNode, InnerNode) { + moofx(ListNode).animate({ + height: InnerNode.offsetHeight, + opacity: 1 + }); + }, + + /** + * Hide products + * + * @param ListNode + */ + hideHiddenArticles: function (ListNode) { + moofx(ListNode).animate({ + height: 0, + opacity: 0 + }); + }, + + /** + * Scroll to the payment section. + * If some requirements are missing, scroll to the next missing label. + * + * @param event + */ + scrollToPayment: function (event) { + event.stop(); + + const Elm = this.getElm(); + const SumNode = Elm.querySelector('.articles-sum-container'); + const RequiredField = Elm.querySelector('.quiqqer-simple-checkout-require'); - if (!PayContainer) { + if (RequiredField) { + RequiredField.scrollIntoView({behavior: "smooth"}); return; } - this.getElm().setStyle('margin-bottom', PayContainer.offsetHeight + 'px'); + SumNode.scrollIntoView({behavior: "smooth"}); } }); }); diff --git a/bin/frontend/controls/SimpleCheckoutWindow.css b/bin/frontend/controls/SimpleCheckoutWindow.css index a47eb3cecb945fbfdcbad688a25c18c12032e175..5b9d22791a3cc405e9c09c49bc184087ec44c259 100644 --- a/bin/frontend/controls/SimpleCheckoutWindow.css +++ b/bin/frontend/controls/SimpleCheckoutWindow.css @@ -30,21 +30,6 @@ } /* order process */ -.SimpleCheckoutWindow .quiqqer-simple-checkout-basket { - top: 0; -} - -.SimpleCheckoutWindow .articles-sum-container { - --_bg: #fff; - font-size: .875rem; - text-align: right; -} - -.SimpleCheckoutWindow .articles-sum tr, -.SimpleCheckoutWindow .articles-sum td { - border-width: 0; -} - -.SimpleCheckoutWindow .quiqqer-simple-checkout-data-pay { - +.SimpleCheckoutWindow .quiqqer-simple-checkout { + --_sticky-positionTop: var(--sticky-positionTop, 0); } \ No newline at end of file diff --git a/locale.xml b/locale.xml index d52f272a2ddee76b8c730fce27e703f596164026..080710ee3ab759d82a21a86192e3d99169a988d7 100644 --- a/locale.xml +++ b/locale.xml @@ -73,6 +73,18 @@ <en><![CDATA[Please enter your address to view available payment methods.]]></en> </locale> + <locale name="ordering.btn.showAllProductsText.open"> + <de><![CDATA[Alle Produkte anzeigen ([max]) <i class="fa-solid fa-chevron-down"></i>]]></de> + <en><![CDATA[Show all products ([max]) <i class="fa-solid fa-chevron-down"></i>]]></en> + </locale> + <locale name="ordering.btn.showAllProductsText.close"> + <de><![CDATA[Weniger anzeigen <i class="fa-solid fa-chevron-up"></i>]]></de> + <en><![CDATA[Show less <i class="fa-solid fa-chevron-up"></i>]]></en> + </locale> + <locale name="ordering.btn.continueWithPurchase"> + <de><![CDATA[Mit Einkauf fortfahren <i class="fa-solid fa-chevron-down"></i>]]></de> + <en><![CDATA[Continue with purchase <i class="fa-solid fa-chevron-down"></i>]]></en> + </locale> </groups> <groups name="quiqqer/order-simple-checkout" datatype="js"> diff --git a/src/QUI/ERP/Order/SimpleCheckout/Basket.ArticleList.html b/src/QUI/ERP/Order/SimpleCheckout/Basket.ArticleList.html index fdba8049e5aca3cceb8457e0ca4f17a3e66f113c..59bcefdba4ca11f99778407587b173c6ced94c71 100644 --- a/src/QUI/ERP/Order/SimpleCheckout/Basket.ArticleList.html +++ b/src/QUI/ERP/Order/SimpleCheckout/Basket.ArticleList.html @@ -3,9 +3,26 @@ </h2> <div class="articleList"> +{assign var=counter value=0} +{assign var=max value=$articles|count} +{assign var=initialVisible value=2} + +{assign var=hideProducts value=false} +{if $max > $initialVisible} + {assign var=hideProducts value=true} +{/if} + {foreach $articles as $Article} {assign var=Product value=\QUI\ERP\Products\Handler\Products::getProduct($Article->getAttribute('id'))} {assign var=calc value=$Article->getAttribute('calculated')} + + {* open html tags for hidden articles *} + {if $hideProducts && $counter == $initialVisible} + <div class="articleList__hidden" style="height: 0; overflow: hidden;"> + <div class="articleList__hiddenInner"> + {/if} + {* END open html tags for hidden articles *} + <section class="articleList-entry"> <div class="articleList-entry__image"> <a href="{$Product->getUrl()}"> @@ -77,9 +94,23 @@ </div> {/if} </div> - </section> + + {* close html tags for hidden articles *} + {if $hideProducts && $counter == $max - 1} + </div> + </div> + {/if} + {* END close html tags for hidden articles *} + + {assign var=counter value=$counter+1} {/foreach} + +{if $counter > $initialVisible} + <button class="articleList__btnShowMore" data-qui-max="{$max}" data-qui-hidden="{$max - $initialVisible}"> + {locale group='quiqqer/order-simple-checkout' var='ordering.btn.showAllProductsText.open' max=$max hidden=$max - $initialVisible} + </button> +{/if} </div> <!-- sum display --> diff --git a/src/QUI/ERP/Order/SimpleCheckout/Basket.css b/src/QUI/ERP/Order/SimpleCheckout/Basket.css index b06584196b8dc4fd607d5575af2e466b0a904858..0f49eb391e4ea731b30cd5f6fe1e6154654060a5 100644 --- a/src/QUI/ERP/Order/SimpleCheckout/Basket.css +++ b/src/QUI/ERP/Order/SimpleCheckout/Basket.css @@ -1,3 +1,7 @@ +.quiqqer-simple-checkout-basket { + --_bg: var(--bg, #fff); +} + /***********/ /* article */ /***********/ @@ -9,8 +13,6 @@ .articleList-entry__image, .articleList-entry__data { - --_bg: var(--bg, #fff); - padding: 1rem; border-radius: 0.5rem; background-color: var(--_bg); @@ -24,6 +26,20 @@ } } +.articleList__btnShowMore { + width: 100%; + text-align: center; + font-size: 0.75rem; +} + +.articleList__btnShowMore, +.articleList__btnShowMore:is(:hover, :active, :focus) { + background-color: transparent; + border: none; + outline: none; + color: var(--qui-color-muted, inherit); +} + /*********/ /* image */ /*********/ @@ -121,26 +137,43 @@ ul.articleData__fields { grid-template-rows: 1fr; } +/*******************/ +/* Hidden articles */ +/*******************/ +.articleList__hiddenInner { + display: flow-root; +} + /*******/ /* sum */ /*******/ -.quiqqer-simple-checkout-orderDetails .articles-sum, -.quiqqer-simple-checkout-basket .articles-sum { - background: var(--_bg, #f8f8f8); +.articles-sum-container { + background-color: var(--_bg); + margin-top: 0.5rem; border-radius: 0.5rem; + padding: 0.75rem 1rem; +} + +.articles-sum { border: none; float: none; + text-align: right; + margin-bottom: 0; + font-size: 0.925rem; } -.quiqqer-simple-checkout-orderDetails .articles-sum td, -.quiqqer-simple-checkout-basket .articles-sum td { - padding-bottom: 0; +.articles-sum tr { + border: none; +} + +.articles-sum td { + padding: 0; + border: none; } -.quiqqer-simple-checkout-orderDetails .articles-sum-row-sum, -.quiqqer-simple-checkout-basket .articles-sum-row-sum { +.articles-sum-row-sum { font-weight: bold; - font-size: 1.25rem; + font-size: 1.125rem; } /****************************/ @@ -149,6 +182,8 @@ ul.articleData__fields { .quiqqer-simple-checkout-basket__toggleBasketBtn { display: flex; justify-content: space-between; + align-items: center; + padding-inline: 0; width: 100%; } diff --git a/src/QUI/ERP/Order/SimpleCheckout/Checkout.css b/src/QUI/ERP/Order/SimpleCheckout/Checkout.css index 6c01ba68afe8eec45680e9f396688cd7563953e8..547122ca9e03339536e31cffc4418881d543046a 100644 --- a/src/QUI/ERP/Order/SimpleCheckout/Checkout.css +++ b/src/QUI/ERP/Order/SimpleCheckout/Checkout.css @@ -1,7 +1,9 @@ .quiqqer-simple-checkout { --_gap: var(--gap, 3rem); + --_sticky-positionTop: var(--sticky-positionTop, 1.5rem); --_sectionSpacing: var(--sectionSpacing, 3rem); --_sectionPadding: var(--sectionPadding, 2rem); + --_sectionBgColor: var(--sectionBgColor, #f8f8f8); --_order-listEntry-padding: var(--order-listEntry-padding, 1rem); --_order-listEntry-bg: var(--order-listEntry-bg, #ffffff); @@ -12,15 +14,10 @@ } @media screen and (max-width: 767px) { - /** - 1. Because of position fixed of .quiqqer-simple-checkout-data-pay on mobile. - This value is override by JavaScript - */ .quiqqer-simple-checkout { --_gap: var(--gap, 1.5rem); --_sectionSpacing: var(--sectionSpacing, 1.5rem); --_sectionPadding: var(--sectionPadding, 1rem); - margin-bottom: 5rem; /* 1 */ } } @@ -101,53 +98,65 @@ background-color: currentColor; } -@media screen and (max-width: 767px) { - .quiqqer-simple-checkout-data-pay { - position: fixed; - left: 0; - bottom: 0; - z-index: 1; - background: #fff; - width: 100%; - box-shadow: 0 0 20px rgba(0, 0, 0, 0.15); - padding: 1rem; - } +.quiqqer-simple-checkout .quiqqer-order-ordering-timeline { + display: none !important; +} + +/*****************/ +/* right: basket */ +/*****************/ +.quiqqer-simple-checkout-basket { + position: sticky; + top: var(--_sticky-positionTop); + align-self: start; +} + +.quiqqer-simple-checkout-basket__inner { + background: var(--_sectionBgColor); + padding: var(--_sectionPadding); +} + +.quiqqer-simple-checkout-basket__pay { + margin-top: 1rem; } -.quiqqer-simple-checkout-data-pay > .btn { +.quiqqer-simple-checkout-basket__pay > :where(.btn) { width: 100%; } -.quiqqer-simple-checkout-data-pay .quiqqer-order-step-checkout-notice { +.quiqqer-simple-checkout-basket__pay > :where(.quiqqer-order-step-checkout-notice) { margin-bottom: 1rem; + background: var(--_sectionBgColor); + padding: var(--_sectionPadding); } -.quiqqer-simple-checkout-data-pay .quiqqer-order-step-checkout-notice label { +.quiqqer-simple-checkout-basket__pay > :where(.quiqqer-order-step-checkout-notice) label { + display: block; + cursor: pointer; +} + +.quiqqer-simple-checkout-basket__pay :where(.quiqqer-order-step-checkout-notice label) { background: transparent; border: none; } -.quiqqer-simple-checkout .quiqqer-order-ordering-timeline { - display: none !important; +/**************************/ +/* Scroll to payments btn */ +/**************************/ +.quiqqer-simple-checkout__scrollToPaymentContainer { + margin-bottom: var(--_sectionSpacing); } -/*****************/ -/* right: basket */ -/*****************/ -.quiqqer-simple-checkout-basket { - --_bg: var(--bg, #f8f8f8); - background: var(--_bg); - padding: var(--_sectionPadding); - align-self: start; - position: sticky; - top: 1.5rem; +.quiqqer-simple-checkout__scrollToPaymentBtn { + display: block; + width: 100%; } /******************************************/ /* Order details for header (only mobile) */ /******************************************/ .quiqqer-simple-checkout-orderDetails { - background-color: #f8f8f8; + background-color: var(--_sectionBgColor); top: 0; padding: var(--_sectionPadding); margin-bottom: var(--_sectionSpacing); diff --git a/src/QUI/ERP/Order/SimpleCheckout/Checkout.html b/src/QUI/ERP/Order/SimpleCheckout/Checkout.html index b2b125a98b41b1364a877532006ad3a382ce9ca0..473c45807e0bc75ffc8dfba9dcdd50aadf30eecf 100644 --- a/src/QUI/ERP/Order/SimpleCheckout/Checkout.html +++ b/src/QUI/ERP/Order/SimpleCheckout/Checkout.html @@ -1,6 +1,13 @@ <div class="quiqqer-simple-checkout-orderDetails" style="opacity: 0"> {$BasketForHeader->create()} </div> + +<div class="hide-on-desktop quiqqer-simple-checkout__scrollToPaymentContainer"> + <button class="quiqqer-simple-checkout__scrollToPaymentBtn btn btn-primary btn-sm btn-small"> + {locale group="quiqqer/order-simple-checkout" var="ordering.btn.continueWithPurchase"} + </button> +</div> + <form method="post" action="" style="opacity: 0"> <div class="quiqqer-simple-checkout-container"> @@ -85,19 +92,6 @@ {$BillingAddress->create()} </div> {/if} - - <div class="quiqqer-simple-checkout-data-pay"> - <div class="quiqqer-order-step-checkout-notice"> - <label> - <input type="checkbox" name="termsAndConditions" required/> - {$termsAndConditions} - </label> - </div> - - <button name="pay" disabled class="btn btn-primary btn-lg btn-large"> - {locale group="quiqqer/order" var="ordering.btn.pay.to.order"} - </button> - </div> </div> </section> @@ -105,6 +99,19 @@ <div class="quiqqer-simple-checkout-basket__inner"> {$Basket->create()} </div> + + <div class="quiqqer-simple-checkout-basket__pay"> + <div class="quiqqer-order-step-checkout-notice"> + <label> + <input type="checkbox" name="termsAndConditions" required/> + {$termsAndConditions} + </label> + </div> + + <button name="pay" disabled class="btn btn-primary btn-lg btn-large"> + {locale group="quiqqer/order" var="ordering.btn.pay.to.order"} + </button> + </div> </section> </div> </form> \ No newline at end of file