From d2c1a7d6eb125b8864fc964289e616e822e3eb9c Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Thu, 1 Feb 2024 06:23:30 -0600 Subject: [PATCH 01/22] First pass --- .../_layouts/components/global-sidebar.twig | 5 +- src/templates/_layouts/cp.twig | 229 ++++++++++-------- src/web/assets/cp/src/css/_cp.scss | 46 ++-- src/web/assets/cp/src/js/CP.js | 3 + 4 files changed, 153 insertions(+), 130 deletions(-) diff --git a/src/templates/_layouts/components/global-sidebar.twig b/src/templates/_layouts/components/global-sidebar.twig index 2d6dd5a8939..76e531fe03e 100644 --- a/src/templates/_layouts/components/global-sidebar.twig +++ b/src/templates/_layouts/components/global-sidebar.twig @@ -21,7 +21,10 @@ } %} {% set linkAttributes = { href: url(item.url), - class: item.sel ? ['sel'] : [], + class: [ + 'nav-list-item', + item.sel ? 'sel', + ] }|merge(item.linkAttributes ?? {}, recursive=true) %}
  • diff --git a/src/templates/_layouts/cp.twig b/src/templates/_layouts/cp.twig index f284824a1e0..c6782ec1d01 100644 --- a/src/templates/_layouts/cp.twig +++ b/src/templates/_layouts/cp.twig @@ -44,16 +44,16 @@ {# The control panel only supports queue components that implement QueueInterface #} {% set queue = craft.app.queue %} {% js %} - {% if queue is instance of("craft\\queue\\QueueInterface") %} - Craft.cp.setJobInfo({{ queue.getJobInfo(100)|json_encode|raw }}, false); - {% if queue.getHasReservedJobs() %} - Craft.cp.trackJobProgress(true); - {% elseif queue.getHasWaitingJobs() %} - Craft.cp.runQueue(); - {% endif %} - {% else %} - Craft.cp.enableQueue = false; +{% if queue is instance of("craft\\queue\\QueueInterface") %} + Craft.cp.setJobInfo({{ queue.getJobInfo(100)|json_encode|raw }}, false); + {% if queue.getHasReservedJobs() %} + Craft.cp.trackJobProgress(true); + {% elseif queue.getHasWaitingJobs() %} + Craft.cp.runQueue(); {% endif %} +{% else %} + Craft.cp.enableQueue = false; +{% endif %} {% endjs %} {% set hasSystemIcon = CraftEdition == CraftPro and craft.rebrand.isIconUploaded %} @@ -120,9 +120,9 @@ {% set userPhoto = include('_layouts/components/header-photo.twig') %} {% js at head %} - // Remove the hash so the browser doesn't scroll to it - window.LOCATION_HASH = document.location.hash ? decodeURIComponent(document.location.hash.substr(1)) : null; - history.replaceState(undefined, undefined, window.location.href.match(/^[^#]*/)[0]); +// Remove the hash so the browser doesn't scroll to it +window.LOCATION_HASH = document.location.hash ? decodeURIComponent(document.location.hash.substr(1)) : null; +history.replaceState(undefined, undefined, window.location.href.match(/^[^#]*/)[0]); {% endjs %} {% block body %} @@ -162,10 +162,16 @@ class="btn menu-toggle" aria-label="{{ 'My Account'|t('app') }}" title="{{ 'My Account'|t('app') }}" - data-disclosure-trigger> + data-disclosure-trigger + > {{ userPhoto|raw }} - @@ -359,7 +376,13 @@ {% endblock %} {% if formActions ?? false %} - + {% include '_layouts/components/form-action-menu' %} {% endif %} @@ -369,6 +392,6 @@ {% if currentUser.can('performUpdates') and not craft.app.updates.getIsUpdateInfoCached() %} {% js %} - Craft.cp.checkForUpdates(); + Craft.cp.checkForUpdates(); {% endjs %} {% endif %} diff --git a/src/web/assets/cp/src/css/_cp.scss b/src/web/assets/cp/src/css/_cp.scss index 705ae5569d3..4a7af010b54 100644 --- a/src/web/assets/cp/src/css/_cp.scss +++ b/src/web/assets/cp/src/css/_cp.scss @@ -111,23 +111,16 @@ $sidebarLinkSecondaryColor: var(--gray-200); overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; - @include light-on-dark-text; - @include light-focus-ring; - background-color: var(--gray-800); + background-color: transparent; + border-right: 1px solid var(--hairline-color); + width: $sidebarWidth; a { - color: $sidebarLinkStaticColor; text-decoration: none; } & > a, *:not(.has-subnav) > a { - &[href] { - &:not(.sel):hover { - background-color: darken($grey800, 5%); - } - } - &:not([href]) { cursor: default; background-color: transparent; @@ -156,7 +149,7 @@ $systemInfoHoverBgColor: darken($grey800, 10%); flex: 0 0 calc(44rem / 16); flex-direction: row; align-items: center; - background-color: $systemInfoBgColor; + color: currentColor; &:after { display: none; @@ -164,7 +157,8 @@ $systemInfoHoverBgColor: darken($grey800, 10%); &:focus, &:hover { - background-color: $systemInfoHoverBgColor !important; + text-decoration: none; + background-color: var(--gray-200); } } @@ -262,8 +256,8 @@ $systemInfoHoverBgColor: darken($grey800, 10%); #nav { flex: 1; - margin: 22px 0 0; - padding-bottom: var(--xl); + margin: var(--s) 0 0; + padding: 0 0 var(--xl) 0; overflow: visible; body.has-debug-toolbar & { @@ -272,9 +266,11 @@ $systemInfoHoverBgColor: darken($grey800, 10%); li { &:not(.has-subnav) > a.sel { - color: var(--gray-800); - background-color: var(--gray-100) !important; opacity: 1; + border-left: 5px solid var(--gray-400); + background-color: transparent; + color: currentColor; + border-radius: 0; .icon { opacity: 1; @@ -282,10 +278,16 @@ $systemInfoHoverBgColor: darken($grey800, 10%); } a { + border-radius: 0; position: relative; + border-left: 5px solid transparent; padding-left: var(--m); padding-right: var(--m); + &[href]:hover { + background-color: var(--gray-050); + } + &:focus { z-index: 1; } @@ -323,10 +325,6 @@ $systemInfoHoverBgColor: darken($grey800, 10%); li a { @include padding(3px, var(--m), 3px, 42px, !important); font-size: 12px; - - &:not(.active) { - color: $sidebarLinkSecondaryColor; - } } } } @@ -489,8 +487,7 @@ li.crumb { #global-header { width: 100%; margin-bottom: var(--m); - @include pane; - background: var(--gray-050); + border-bottom: 1px solid var(--hairline-color); .flex { height: calc(44rem / 16); @@ -983,7 +980,6 @@ li.breadcrumb-toggle-wrapper { padding: var(--s) var(--padding); position: relative; z-index: 2; - width: calc(100vw - #{$sidebarWidth}); box-sizing: border-box; background-color: transparentize($grey300, 1); box-shadow: 0 1px 0 transparentize($grey800, 1); @@ -1097,7 +1093,6 @@ li.breadcrumb-toggle-wrapper { display: flex; flex-direction: row; align-items: flex-start; - width: calc(100vw - #{$sidebarWidth}); padding: 0 var(--padding) 48px; box-sizing: border-box; @@ -1569,13 +1564,12 @@ li.breadcrumb-toggle-wrapper { &:not(.has-subnav) > a { &:not(.sel):hover { text-decoration: none; - background-color: var(--gray-100); } &.sel, &.active-drop-target { cursor: default; - background-color: var(--gray-500) !important; + background-color: var(--gray-500); &, & + .toggle { diff --git a/src/web/assets/cp/src/js/CP.js b/src/web/assets/cp/src/js/CP.js index 994ac6c0cf6..7983ef734b3 100644 --- a/src/web/assets/cp/src/js/CP.js +++ b/src/web/assets/cp/src/js/CP.js @@ -832,6 +832,7 @@ Craft.CP = Garnish.Base.extend( this.$headerContainer[0].getBoundingClientRect().top < 0 ) { const headerHeight = this.$headerContainer.height(); + const headerWidth = this.$header.width(); if (!this.fixedHeader) { // Hard-set the minimum content container height this.$contentContainer.css( @@ -841,6 +842,7 @@ Craft.CP = Garnish.Base.extend( // Hard-set the header container height this.$headerContainer.height(headerHeight); + this.$header.width(headerWidth); Garnish.$bod.addClass('fixed-header'); this.fixedHeader = true; @@ -850,6 +852,7 @@ Craft.CP = Garnish.Base.extend( this._setFixedTopPos(this.$details, headerHeight); } else if (this.fixedHeader) { this.$headerContainer.height('auto'); + this.$header.width('auto'); Garnish.$bod.removeClass('fixed-header'); this.$contentContainer.css('min-height', ''); this.$sidebar.removeClass('fixed').css('top', ''); From 63d3c0cd655559c551fb78fb4378a4d225bc653e Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Fri, 2 Feb 2024 11:56:25 -0600 Subject: [PATCH 02/22] New sidebar styling --- src/templates/_layouts/base.twig | 4 + .../_layouts/components/global-sidebar.twig | 221 ++++++++-------- .../_layouts/components/system-info.twig | 12 + src/web/assets/cp/src/Craft.js | 3 + src/web/assets/cp/src/css/_cp.scss | 86 +++---- .../assets/cp/src/css/_global-sidebar.scss | 235 ++++++++++++++++++ src/web/assets/cp/src/css/craft.scss | 1 + .../assets/cp/src/js/CraftGlobalSidebar.js | 27 ++ 8 files changed, 440 insertions(+), 149 deletions(-) create mode 100644 src/templates/_layouts/components/system-info.twig create mode 100644 src/web/assets/cp/src/css/_global-sidebar.scss create mode 100644 src/web/assets/cp/src/js/CraftGlobalSidebar.js diff --git a/src/templates/_layouts/base.twig b/src/templates/_layouts/base.twig index dd516b0a007..249f096aa01 100644 --- a/src/templates/_layouts/base.twig +++ b/src/templates/_layouts/base.twig @@ -15,8 +15,12 @@ {% set bodyAttributes = { class: bodyClass, dir: orientation, + data: { + sidebar: 'expanded', + } }|merge(bodyAttributes ?? {}, recursive=true) -%} + {% do view.registerAssetBundle('craft\\web\\assets\\cp\\CpAsset') -%} {% set cpAssetUrl = view.getAssetManager().getPublishedUrl('@app/web/assets/cp/dist', true) -%} diff --git a/src/templates/_layouts/components/global-sidebar.twig b/src/templates/_layouts/components/global-sidebar.twig index 76e531fe03e..b91bb598202 100644 --- a/src/templates/_layouts/components/global-sidebar.twig +++ b/src/templates/_layouts/components/global-sidebar.twig @@ -1,110 +1,129 @@ -
  • + {% endfor %} + + + - {% if currentUser.admin and devMode %} - {% set devModeText = 'Craft CMS is running in Dev Mode.'|t('app') %} -
    - {{ tag('span', { - class: 'visually-hidden', - text: devModeText - }) }} + - {% endif %} - + + diff --git a/src/templates/_layouts/components/system-info.twig b/src/templates/_layouts/components/system-info.twig new file mode 100644 index 00000000000..fe0495403be --- /dev/null +++ b/src/templates/_layouts/components/system-info.twig @@ -0,0 +1,12 @@ + +
    + {% if hasSystemIcon %} + + {% else %} + {{ iconSvg('c-outline') }} + {% endif %} +
    +
    + {{ systemName }} +
    +
    diff --git a/src/web/assets/cp/src/Craft.js b/src/web/assets/cp/src/Craft.js index 48d25481cff..4997a14a7e9 100644 --- a/src/web/assets/cp/src/Craft.js +++ b/src/web/assets/cp/src/Craft.js @@ -92,3 +92,6 @@ import './js/Tooltip.js'; import './js/Uploader.js'; import './js/UriFormatGenerator.js'; import './js/UserIndex.js'; + +// Custom elements +import './js/CraftGlobalSidebar.js'; diff --git a/src/web/assets/cp/src/css/_cp.scss b/src/web/assets/cp/src/css/_cp.scss index 4a7af010b54..b375f83917c 100644 --- a/src/web/assets/cp/src/css/_cp.scss +++ b/src/web/assets/cp/src/css/_cp.scss @@ -1,16 +1,6 @@ @charset "UTF-8"; @import '@craftcms/sass/mixins'; -:root { - --xs: 4px; - --s: 8px; - --m: 14px; - --l: 18px; - --xl: 24px; - --padding: var(--xl); - --neg-padding: calc(var(--padding) * -1); -} - $sidebarWidth: 226px; $sidebarZIndex: 100; $detailsWidth: 350px; @@ -23,6 +13,17 @@ $minHorizontalUiWidth: $minFullUiWidth - $sidebarWidth; $badgeBackgroundColor: var(--gray-200); $badgeColor: var(--gray-800); +:root { + --xs: 4px; + --s: 8px; + --m: 14px; + --l: 18px; + --xl: 24px; + --padding: var(--xl); + --neg-padding: calc(var(--padding) * -1); + --sidebar-width: #{$sidebarWidth}; +} + html { -webkit-text-size-adjust: 100%; min-height: 100vh; @@ -81,7 +82,7 @@ body { #global-container { position: relative; display: flex; - flex-direction: column; + //flex-direction: column; min-height: 100vh; @include left(0); @@ -100,42 +101,6 @@ body { $sidebarLinkStaticColor: var(--gray-100); $sidebarLinkSecondaryColor: var(--gray-200); -#global-sidebar { - --is-always-visible: true; - position: fixed; - z-index: $sidebarZIndex; - display: flex; - flex-direction: column; - height: 100vh; - padding: 0; - overflow-y: auto; - overflow-x: hidden; - -webkit-overflow-scrolling: touch; - background-color: transparent; - border-right: 1px solid var(--hairline-color); - width: $sidebarWidth; - - a { - text-decoration: none; - } - - & > a, - *:not(.has-subnav) > a { - &:not([href]) { - cursor: default; - background-color: transparent; - } - } - - .light { - color: var(--gray-400); - } - - @media only screen and (max-width: $minFullUiWidth - 1px) { - --is-always-visible: false; - } -} - $systemInfoBgColor: darken($grey800, 5%); $systemInfoHoverBgColor: darken($grey800, 10%); @@ -216,6 +181,30 @@ $systemInfoHoverBgColor: darken($grey800, 10%); } } } + + [data-sidebar='collapsed'] #system-info:hover & { + opacity: 1; + transform: translateY(-50%) translateX(0); + } + + [data-sidebar='collapsed'] & { + position: absolute; + left: calc(100% + (var(--s) * 2)); + top: 50%; + transform: translateY(-50%) translateX(-5px); + opacity: 0; + @include pane; + color: var(--text-color); + border-radius: var(--medium-border-radius); + padding: var(--xs) var(--s); + white-space: nowrap; + + @media screen and (prefers-reduced-motion: no-preference) { + transition: + opacity 50ms cubic-bezier(0.33, 1, 0.68, 1), + transform 150ms cubic-bezier(0.33, 1, 0.68, 1); + } + } } #job-icon { @@ -401,6 +390,7 @@ $systemInfoHoverBgColor: darken($grey800, 10%); } #devmode { + margin-top: auto; flex: 0 0 4px; width: 100%; min-height: 4px; // fixes Windows scaling bug (https://github.com/craftcms/cms/issues/3259) @@ -409,7 +399,7 @@ $systemInfoHoverBgColor: darken($grey800, 10%); } #page-container { - @include padding-left($sidebarWidth); + //@include padding-left($sidebarWidth); display: flex; flex-direction: column; flex-grow: 1; diff --git a/src/web/assets/cp/src/css/_global-sidebar.scss b/src/web/assets/cp/src/css/_global-sidebar.scss new file mode 100644 index 00000000000..0bc212e46b0 --- /dev/null +++ b/src/web/assets/cp/src/css/_global-sidebar.scss @@ -0,0 +1,235 @@ +.global-sidebar { + --is-always-visible: true; + --collapsed-width: 50px; + --prefix-size: calc(var(--collapsed-width) * 0.66); + --prefix-ratio: 1; + + position: relative; + z-index: $sidebarZIndex; + display: flex; + flex-direction: column; + height: 100vh; + padding: 0; + -webkit-overflow-scrolling: touch; + background-color: transparent; + border-right: 1px solid var(--hairline-color); + width: var(--sidebar-width); + + @media screen and (prefers-reduced-motion: no-preference) { + //transition: width 500ms cubic-bezier(0.33, 1, 0.68, 1); + } + + [data-sidebar='collapsed'] & { + width: var(--collapsed-width); + } + + a { + text-decoration: none; + } + + & > a, + *:not(.has-subnav) > a { + &:not([href]) { + cursor: default; + background-color: transparent; + } + } + + .light { + color: var(--gray-400); + } + + @media only screen and (max-width: $minFullUiWidth - 1px) { + --is-always-visible: false; + } +} + +.global-sidebar__nav { + padding: var(--s) 0; +} + +.global-sidebar__footer { + margin-block-start: auto; + display: grid; + gap: var(--s); +} + +.global-nav > ul { + display: flex; + flex-direction: column; + gap: 2px; +} + +.global-nav > ul > li { + position: relative; + padding: 0 var(--s); +} + +.global-nav > ul > li.sel { +} + +.nav-icon { + display: inline-block; + width: 1rem; + aspect-ratio: 1; + line-height: 0; + + --thumb-size: 1rem; + + > svg { + @include svg-mask(currentColor); + max-width: 100%; + } +} + +.nav-item { +} + +.nav-item__subnav { + --prefix-ratio: 4/3; + + [data-sidebar='collapsed'] & { + display: none; + } +} + +.badge { + flex-shrink: 1; + padding: 0 6px; + font-size: 11px; + line-height: 16px; + border-radius: var(--s); + background-color: var(--medium-text-color); + color: var(--white); +} + +.sidebar-actions { + padding: var(--s); +} + +.sidebar-action { + --thumb-size: 0.75rem; + display: flex; + width: 100%; + align-items: center; + position: relative; + text-align: left; + border-radius: var(--medium-border-radius); + color: currentColor; + cursor: pointer; + + &:before { + content: ''; + position: absolute; + left: calc(var(--xs) * -2); + top: 50%; + height: 80%; + transform: translateY(-50%); + width: var(--xs); + } + + &:hover { + background-color: var(--gray-200); + } + + &.sel { + &:before { + background-color: currentColor; + } + } +} + +.sidebar-action--sub { + &:before { + width: 7px; + height: 7px; + border-radius: 100%; + left: 13px; // magic number to align with center of the prefix icons + } +} + +.sidebar-action__prefix { + display: flex; + align-items: center; + justify-content: center; + width: var(--prefix-size); + aspect-ratio: var(--prefix-ratio); + line-height: 0; + + .nav-item__subnav { + } +} + +.sidebar-action__label { + display: inline-flex; + gap: var(--s); + align-items: center; + padding: 0 var(--xs); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex-grow: 1; +} + +.sidebar-action__badge { + padding: 0 var(--s); + margin-left: auto; +} + +/** +States + */ +[data-sidebar='collapsed'] { + .sidebar-action { + flex-direction: column; + justify-content: center; + + &:hover { + .sidebar-action__label { + opacity: 1; + transform: translateY(-50%) translateX(0); + } + } + } + + .sidebar-action__label { + position: absolute; + left: calc(100% + (var(--s) * 2)); + top: 50%; + transform: translateY(-50%) translateX(-5px); + opacity: 0; + @include pane; + color: var(--text-color); + border-radius: var(--medium-border-radius); + padding: var(--xs) var(--s); + + @media screen and (prefers-reduced-motion: no-preference) { + transition: + opacity 50ms cubic-bezier(0.33, 1, 0.68, 1), + transform 150ms cubic-bezier(0.33, 1, 0.68, 1); + } + } + + .sidebar-action__badge { + z-index: 100; + position: absolute; + right: 0; + bottom: 0; + transform: translateY(25%); + padding: 0; + } +} + +[data-sidebar='expanded'] { + #sidebar-toggle-icon { + transform: rotate(180deg); + } +} + +#sidebar-trigger { + display: none; + + @media only screen and (min-width: $minFullUiWidth) { + display: flex; + } +} diff --git a/src/web/assets/cp/src/css/craft.scss b/src/web/assets/cp/src/css/craft.scss index 41b1313177d..5a0c21a821c 100644 --- a/src/web/assets/cp/src/css/craft.scss +++ b/src/web/assets/cp/src/css/craft.scss @@ -4,6 +4,7 @@ @import 'variables'; @import 'main'; @import 'cp'; +@import 'global-sidebar'; @import 'preview'; @import 'login'; @import 'fld'; diff --git a/src/web/assets/cp/src/js/CraftGlobalSidebar.js b/src/web/assets/cp/src/js/CraftGlobalSidebar.js new file mode 100644 index 00000000000..069315b3cac --- /dev/null +++ b/src/web/assets/cp/src/js/CraftGlobalSidebar.js @@ -0,0 +1,27 @@ +class CraftGlobalSidebar extends HTMLElement { + connectedCallback() { + this.trigger = this.querySelector('#sidebar-trigger'); + + if (this.trigger) { + this.trigger.addEventListener('click', this.toggle.bind(this)); + } + } + + toggle() { + if (document.body.getAttribute('data-sidebar') === 'expanded') { + this.collapse(); + } else { + this.expand(); + } + } + + expand() { + document.body.setAttribute('data-sidebar', 'expanded'); + } + + collapse() { + document.body.setAttribute('data-sidebar', 'collapsed'); + } +} + +customElements.define('craft-global-sidebar', CraftGlobalSidebar); From 4a8d575b8dc7fcd16ff983ec65f83b1ac8a9b7cd Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Fri, 2 Feb 2024 14:28:36 -0600 Subject: [PATCH 03/22] Cookie functionality --- src/templates/_layouts/base.twig | 2 +- src/templates/_layouts/cp.twig | 204 +++++++++--------- src/web/assets/cp/src/css/_cp.scss | 79 +++---- .../assets/cp/src/css/_global-sidebar.scss | 86 ++++---- .../assets/cp/src/js/CraftGlobalSidebar.js | 2 + 5 files changed, 182 insertions(+), 191 deletions(-) diff --git a/src/templates/_layouts/base.twig b/src/templates/_layouts/base.twig index 249f096aa01..b8f7f22a717 100644 --- a/src/templates/_layouts/base.twig +++ b/src/templates/_layouts/base.twig @@ -16,7 +16,7 @@ class: bodyClass, dir: orientation, data: { - sidebar: 'expanded', + sidebar: craft.app.request.rawCookies.value('Craft-' ~ craft.app.systemUid ~ ':sidebar') } }|merge(bodyAttributes ?? {}, recursive=true) -%} diff --git a/src/templates/_layouts/cp.twig b/src/templates/_layouts/cp.twig index c6782ec1d01..5a0d09b4b03 100644 --- a/src/templates/_layouts/cp.twig +++ b/src/templates/_layouts/cp.twig @@ -196,120 +196,122 @@ history.replaceState(undefined, undefined, window.location.href.match(/^[^#]*/)[
    -
    +
    +
    -
    +
    - {% if fullPageForm -%} -
    - {{- csrfInput() }} - {%- endif %} - - {% if showHeader %} -
    -
    {% endif %} - {# content-container #} -
    - {% if sidebar %} -

    - {% endif %} - {% block main %} - {% if errorSummary is not empty %} - {{ errorSummary is defined ? errorSummary|raw }} +
    + {# sidebar #} + {% if sidebar %} + + {% endif %} -
    - {% if contentNotice or tabs %} -
    - {{ contentNotice ? tag('div', { - id: 'content-notice', - html: contentNotice, - role: 'status', - }) }} - {% if tabs %} - {% include "_includes/tabs" with { - containerAttributes: { - id: 'tabs', - }, - } %} - {% endif %} -
    + + {# content-container #} +
    + {% if sidebar %} +

    {% endif %} + {% block main %} + {% if errorSummary is not empty %} + {{ errorSummary is defined ? errorSummary|raw }} + {% endif %} +
    + {% if contentNotice or tabs %} +
    + {{ contentNotice ? tag('div', { + id: 'content-notice', + html: contentNotice, + role: 'status', + }) }} + {% if tabs %} + {% include "_includes/tabs" with { + containerAttributes: { + id: 'tabs', + }, + } %} + {% endif %} +
    + {% endif %} - {% block content %} - {{ content is defined ? content|raw }} - {% endblock %} + {% block content %} + {{ content is defined ? content|raw }} + {% endblock %} - {# footer #} - {% if footer %} - + {% endblock %} +
    + + {% if details is not empty %} +
    +
    +
    + {{ details|raw }} +
    - {% endif %} -
    - {% endblock %} -
    - - {% if details is not empty %} -
    -
    -
    - {{ details|raw }}
    -
    -
    - {% endif %} -
    + {% endif %} +
    - {% if fullPageForm -%} - - {%- endif %} -
    -
    + {% if fullPageForm -%} + + {%- endif %} + +
    +