{% sw_extends '@Storefront/storefront/component/checkout/offcanvas-cart.html.twig' %} {# Remove the entire Shopware offcanvas close button — our cart renders its own header #} {% block utilities_offcanvas_close %}{% endblock %} {# ───────────────────────────────────────────────────────────────────────────── Ultimate Cart — offcanvas override (Shopware 6.7) Template path: component/checkout/offcanvas-cart.html.twig ───────────────────────────────────────────────────────────────────────────── #} {% set uc = page.extensions.ultimateCart ?? null %} {% block component_offcanvas_cart %} {# Drawer width override lives in scss/_cart.scss (.offcanvas) #} {% if uc and uc.enabled %} {% set ucTheme = uc.theme ?? 'classic' %} {# position:relative already comes from the .uc-cart class rule in _cart.scss — no need to repeat it here. Every other value below is merchant-configured and dynamic, so it's exposed as data-* attributes instead of an inline style="" block; UltimateCartPlugin ._initThemeVars() applies the equivalent CSS custom properties via el.style.setProperty() at runtime. #}
{# Milestone-crossed detector data — see celebration-data.html.twig. Rendered unconditionally (not gated by progressBarEnabled) so it survives every AJAX swap of this drawer's markup regardless of which visual elements the merchant has toggled on. #} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/celebration-data.html.twig' with { uc: uc } only %} {# ── Cart header ── #}
{{ 'sellops-ultimate-cart.cart.title'|trans }} {% set itemCount = page.cart.lineItems.filterFlatByType('product')|length %} {% if itemCount > 0 %} {{ itemCount }} {% endif %}
{# ── Scrollable body ── #}
{% if page.cart.lineItems.filterFlatByType('product')|length == 0 %}

{{ 'sellops-ultimate-cart.cart.emptyTitle'|trans }}

{{ 'sellops-ultimate-cart.cart.emptySubtitle'|trans }} {{ 'sellops-ultimate-cart.cart.startShopping'|trans }}
{% else %} {% set elementOrder = uc.elementOrder|length > 0 ? uc.elementOrder : ['trust-bar','timer','progress-bar','gift-offers','line-items','cross-sell','usp-grid','discount','order-summary','custom-html','custom-html-2','custom-html-3'] %} {# Back-compat: saved orders from before 'gift-offers' existed — render it after the progress bar #} {% if 'gift-offers' not in elementOrder %} {% set newOrder = [] %} {% for key in elementOrder %} {% set newOrder = newOrder|merge([key]) %} {% if key == 'progress-bar' %} {% set newOrder = newOrder|merge(['gift-offers']) %} {% endif %} {% endfor %} {% set elementOrder = newOrder %} {% endif %} {# Back-compat: saved orders from before 'usp-grid' existed — render it after cross-sell #} {% if 'usp-grid' not in elementOrder %} {% set newOrder2 = [] %} {% for key in elementOrder %} {% set newOrder2 = newOrder2|merge([key]) %} {% if key == 'cross-sell' %} {% set newOrder2 = newOrder2|merge(['usp-grid']) %} {% endif %} {% endfor %} {% set elementOrder = newOrder2 %} {% endif %} {% set disabledElements = uc.disabledElements ?? [] %} {% for elementKey in elementOrder %} {% if elementKey in disabledElements %} {# Element disabled via admin sorter toggle — skip #} {% elseif elementKey == 'trust-bar' and uc.trustBarEnabled %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/trust-bar.html.twig' with { uc: uc } only %} {% elseif elementKey == 'timer' and uc.timerEnabled %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/timer.html.twig' with { uc: uc } only %} {% elseif elementKey == 'progress-bar' and uc.progressBarEnabled %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/progress-bar.html.twig' with { uc: uc, cartTotal: page.cart.price.totalPrice } only %} {% elseif elementKey == 'gift-offers' %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/gift-offers.html.twig' with { uc: uc } only %} {% elseif elementKey == 'line-items' %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/line-items.html.twig' with { uc: uc, lineItems: page.cart.lineItems } only %} {% elseif elementKey == 'discount' and uc.discountEnabled %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/discount.html.twig' with { uc: uc, promotions: page.cart.lineItems.filterFlatByType('promotion') } only %} {% elseif elementKey == 'order-summary' and uc.orderSummaryEnabled %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/order-summary.html.twig' with { uc: uc, cart: page.cart } only %} {% elseif elementKey == 'cross-sell' and uc.crossSellEnabled %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/cross-sell.html.twig' with { uc: uc } only %} {% elseif elementKey == 'usp-grid' and uc.uspGridEnabled %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/usp-grid.html.twig' with { uc: uc } only %} {% elseif elementKey == 'custom-html' and uc.customHtmlEnabled %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/custom-html.html.twig' with { uc: uc } only %} {% elseif elementKey == 'custom-html-2' and uc.customHtmlEnabled2 %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/custom-html-2.html.twig' with { uc: uc } only %} {% elseif elementKey == 'custom-html-3' and uc.customHtmlEnabled3 %} {% include '@SellopsUltimateCart/storefront/component/ultimate-cart/elements/custom-html-3.html.twig' with { uc: uc } only %} {% endif %} {% endfor %} {% endif %}
{# /.uc-cart__body #} {# ── Sticky footer ────────────────────────────────────────────── Checkout button is our own controlled markup — stays as-is. "View Cart" + any payment-plugin express buttons: we re-open Shopware's own native block name, component_offcanvas_cart_actions_cart, and call {{ parent() }} — nothing else. This is a generic, vendor-agnostic hook (no plugin-specific file/class/block referenced). Confirmed against SwagPayPal's real compiled Twig output on your stage server: its override of this exact block also opens with a call to parent() (native content) before appending its own express-button markup. Because both overrides target the identical native block name, Twig's own "next template in the sw_extends chain" resolution is what decides whether parent() reaches PayPal's addition or skips straight to core — that's a caching/resolution-order question, not something either override controls directly. Earlier attempts that failed (blank footer, missing checkout button, plain "Proceed to Cart" only) all either used the wrong block name or didn't call parent() at all. This is the correct, minimal form. After deploying, run a FULL cache clear (see below) before retesting — a stale compiled Twig cache will look identical to a real bug. #} {% if page.cart.lineItems.filterFlatByType('product')|length > 0 %} {% endif %}
{# /.uc-cart #} {% else %} {# Plugin disabled — render default Shopware cart #} {{ parent() }} {% endif %} {% endblock %}