diff --git a/advancedmegamenu.php b/advancedmegamenu.php
index cb1da6e..d70c1c4 100644
--- a/advancedmegamenu.php
+++ b/advancedmegamenu.php
@@ -716,6 +716,10 @@ class AdvancedMegaMenu extends Module implements WidgetInterface
switch ($type) {
case 'category':
+ if ($customLink !== '') {
+ return $customLink;
+ }
+
return $entityId > 0 ? $this->context->link->getCategoryLink($entityId) : '#';
case 'cms':
if ($entityId <= 0) {
diff --git a/views/css/admin.css b/views/css/admin.css
index 26997eb..e9eac7c 100644
--- a/views/css/admin.css
+++ b/views/css/admin.css
@@ -1,4 +1,16 @@
+.adv-megamenu-admin__intro {
+ margin-bottom: 1.5rem;
+}
+
+.adv-megamenu-admin__intro-rule {
+ color: #8a6d3b;
+}
+
.adv-megamenu-admin__toolbar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
margin-bottom: 1rem;
}
@@ -7,64 +19,309 @@
gap: 1rem;
}
-.adv-menu-node {
- border: 1px solid #d9dee3;
- border-radius: 8px;
- background: #fff;
+.adv-megamenu-admin__empty {
+ padding: 2rem;
+ border: 1px dashed #c8d2dc;
+ border-radius: 10px;
+ background: #fafbfd;
+ color: #6c7a89;
+ text-align: center;
}
-.adv-menu-node__header,
-.adv-menu-layout__header {
+.adv-menu-admin-root-list,
+.adv-menu-admin-children-list,
+.adv-menu-admin-layout-list {
+ display: grid;
+ gap: 1rem;
+}
+
+.adv-menu-admin-root-shell {
+ display: grid;
+ gap: 1rem;
+}
+
+.adv-menu-admin-root-tabs {
+ display: flex;
+ gap: 0.75rem;
+ overflow-x: auto;
+ padding-bottom: 0.25rem;
+}
+
+.adv-menu-admin-root-tab {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
- padding: 0.9rem 1rem;
- background: #f7f8f9;
- border-bottom: 1px solid #e7eaee;
+ min-width: 240px;
+ padding: 0.85rem 1rem;
+ border: 1px solid #d9dee3;
+ border-radius: 12px;
+ background: linear-gradient(180deg, #ffffff 0%, #f7fafc 100%);
+ text-align: left;
+ box-shadow: 0 4px 14px rgba(32, 43, 56, 0.04);
}
-.adv-menu-node__body,
-.adv-menu-layout__body {
- padding: 1rem;
+.adv-menu-admin-root-tab.is-active {
+ border-color: #5c7c9c;
+ background: linear-gradient(135deg, #eef4fa 0%, #dfeaf5 100%);
+ box-shadow: 0 8px 22px rgba(92, 124, 156, 0.18);
}
-.adv-menu-node__grid,
-.adv-menu-layout__grid {
- display: grid;
- gap: 1rem;
- grid-template-columns: repeat(2, minmax(0, 1fr));
+.adv-menu-admin-root-tab__main {
+ min-width: 0;
}
-.adv-menu-node__actions,
-.adv-menu-layout__actions {
+.adv-menu-admin-root-tab__title {
+ display: block;
+ font-weight: 600;
+ color: #22303c;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.adv-menu-admin-root-tab__meta {
+ display: block;
+ margin-top: 0.25rem;
+ color: #66788a;
+ font-size: 12px;
+}
+
+.adv-menu-admin-root-tab__actions {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.35rem;
+}
+
+.adv-menu-admin-root-tab__actions .btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 28px;
+ height: 28px;
+ padding: 0;
+}
+
+.adv-menu-admin-root-panel {
+ min-width: 0;
+}
+
+.adv-menu-admin-node {
+ border: 1px solid #d9dee3;
+ border-radius: 10px;
+ background: #fff;
+ box-shadow: 0 4px 14px rgba(32, 43, 56, 0.04);
+}
+
+.adv-menu-admin-node--root > .adv-menu-admin-node__header {
+ background: linear-gradient(135deg, #f8fafc 0%, #eef3f8 100%);
+}
+
+.adv-menu-admin-node__header,
+.adv-menu-admin-layout__header {
display: flex;
- gap: 0.5rem;
- flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: flex-start;
+ gap: 1rem;
+ padding: 1rem 1.1rem;
+ border-bottom: 1px solid #edf1f4;
}
-.adv-menu-node__children,
-.adv-menu-node__layouts {
+.adv-menu-admin-node__header-main,
+.adv-menu-admin-layout__header-main {
+ min-width: 0;
+}
+
+.adv-menu-admin-node__title,
+.adv-menu-admin-layout__title {
+ display: flex;
+ align-items: center;
+ gap: 0.55rem;
+ margin: 0;
+ font-size: 16px;
+ line-height: 1.2;
+}
+
+.adv-menu-admin-node__title-text,
+.adv-menu-admin-layout__title-text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.adv-menu-admin-node__meta,
+.adv-menu-admin-layout__meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ margin-top: 0.6rem;
+}
+
+.adv-menu-admin-node__meta-item,
+.adv-menu-admin-layout__meta-item {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.35rem;
+ padding: 0.2rem 0.55rem;
+ border-radius: 999px;
+ background: #f1f5f8;
+ color: #52606d;
+ font-size: 12px;
+}
+
+.adv-menu-admin-node__actions,
+.adv-menu-admin-layout__actions {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ gap: 0.45rem;
+}
+
+.adv-menu-admin-action {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+}
+
+.adv-menu-admin-node__body,
+.adv-menu-admin-layout__body {
+ padding: 1rem 1.1rem 1.1rem;
+}
+
+.adv-menu-admin-node__summary {
+ display: grid;
+ gap: 0.75rem;
+ grid-template-columns: minmax(0, 1fr) auto;
+ align-items: start;
+}
+
+.adv-menu-admin-node__link {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ color: #495867;
+ font-size: 13px;
+ word-break: break-all;
+}
+
+.adv-menu-admin-node__image,
+.adv-menu-image-preview {
+ max-width: 140px;
+ border-radius: 8px;
+ border: 1px solid #edf1f4;
+}
+
+.adv-menu-admin-node__collections {
display: grid;
gap: 1rem;
margin-top: 1rem;
}
-.adv-menu-node__children {
- padding-left: 1.25rem;
- border-left: 2px solid #e7eaee;
+.adv-menu-admin-node__children {
+ padding-left: 1rem;
+ border-left: 2px solid #edf1f4;
}
-.adv-menu-language {
- border: 1px dashed #d9dee3;
- border-radius: 6px;
- padding: 0.8rem;
+.adv-menu-admin-node__children--grid {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ align-items: start;
}
-.adv-menu-image-preview {
- max-width: 180px;
- margin-top: 0.6rem;
- border-radius: 6px;
+.adv-menu-admin-node--compact .adv-menu-admin-node__header {
+ padding: 0.8rem 0.9rem;
+}
+
+.adv-menu-admin-node--compact .adv-menu-admin-node__body {
+ padding: 0.85rem 0.9rem 0.9rem;
+}
+
+.adv-menu-admin-node--compact .adv-menu-admin-node__title {
+ font-size: 14px;
+}
+
+.adv-menu-admin-node--compact .adv-menu-admin-node__summary {
+ grid-template-columns: 1fr;
+}
+
+.adv-menu-admin-node--compact .adv-menu-admin-node__actions {
+ gap: 0.35rem;
+}
+
+.adv-menu-admin-node--compact .adv-menu-admin-action {
+ padding-left: 0.45rem;
+ padding-right: 0.45rem;
+}
+
+.adv-menu-admin-node--compact .adv-menu-admin-action span {
+ display: none;
+}
+
+.adv-menu-admin-node--compact .adv-menu-admin-node__meta {
+ gap: 0.35rem;
+}
+
+.adv-menu-admin-node--compact .adv-menu-admin-node__meta-item {
+ font-size: 11px;
+}
+
+.adv-menu-admin-node__section-title {
+ display: flex;
+ align-items: center;
+ gap: 0.45rem;
+ margin: 0 0 0.75rem;
+ color: #2f3d4a;
+ font-size: 13px;
+ text-transform: uppercase;
+ letter-spacing: 0.03em;
+}
+
+.adv-menu-admin-layout {
+ border: 1px solid #e3e8ed;
+ border-radius: 8px;
+ background: #fbfcfd;
+}
+
+.adv-megamenu-admin__modal-grid {
+ display: grid;
+ gap: 1rem;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
+.adv-megamenu-admin__checkbox {
+ display: flex;
+ align-items: center;
+ gap: 0.6rem;
+ padding-top: 2rem;
+}
+
+.adv-megamenu-admin__checkbox input {
+ margin: 0;
+}
+
+.adv-megamenu-admin__modal-image {
+ margin-top: 1.25rem;
+}
+
+.adv-megamenu-admin__lang-panels {
+ display: grid;
+ gap: 1rem;
+ margin-top: 1.25rem;
+}
+
+.adv-megamenu-admin__lang-panel {
+ border: 1px solid #e3e8ed;
+ border-radius: 8px;
+ padding: 1rem;
+ background: #fcfdff;
+}
+
+.adv-megamenu-admin__lang-panel h5 {
+ margin: 0 0 0.85rem;
+}
+
+.adv-megamenu-admin__lang-grid {
+ display: grid;
+ gap: 1rem;
}
.adv-menu-products {
@@ -83,6 +340,13 @@
background: #eef3f7;
}
+.adv-menu-product-chip button {
+ border: 0;
+ background: transparent;
+ padding: 0;
+ line-height: 1;
+}
+
.adv-menu-product-results {
margin-top: 0.5rem;
border: 1px solid #d9dee3;
@@ -104,9 +368,32 @@
border-top: 1px solid #eef2f5;
}
+.adv-menu-product-results button:hover {
+ background: #f6f9fc;
+}
+
@media (max-width: 991px) {
- .adv-menu-node__grid,
- .adv-menu-layout__grid {
+ .adv-menu-admin-node__summary,
+ .adv-megamenu-admin__modal-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .adv-menu-admin-node__actions,
+ .adv-menu-admin-layout__actions {
+ justify-content: flex-start;
+ }
+
+ .adv-menu-admin-root-tab {
+ min-width: 200px;
+ }
+
+ .adv-menu-admin-node__children--grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 767px) {
+ .adv-menu-admin-node__children--grid {
grid-template-columns: 1fr;
}
}
diff --git a/views/css/megamenu.css b/views/css/megamenu.css
index 1bd0f07..ab99202 100644
--- a/views/css/megamenu.css
+++ b/views/css/megamenu.css
@@ -1,4 +1,26 @@
.adv-megamenu {
+ --adv-root-gap: 0.25rem;
+ --adv-root-link-gap: 0.45rem;
+ --adv-root-link-min-height: 50px;
+ --adv-root-link-padding-x: 0.7rem;
+ --adv-root-font-size: 0.9rem;
+ --adv-panel-inner-gap: 1.25rem;
+ --adv-panel-inner-padding: 1rem;
+ --adv-panel-nav-min: 240px;
+ --adv-panel-nav-max: 280px;
+ --adv-submenu-title-size: 1rem;
+ --adv-rich-intro-gap: 0.9rem;
+ --adv-rich-intro-padding: 0.95rem;
+ --adv-rich-intro-media-width: 180px;
+ --adv-rich-intro-title-size: 1.12rem;
+ --adv-card-title-size: 0.95rem;
+ --adv-card-text-size: 0.88rem;
+ --adv-product-name-size: 0.8rem;
+ --adv-node-card-image-size: 88px;
+ --adv-node-card-body-padding: 0.5rem;
+ --adv-node-card-gap: 0.3rem;
+ --adv-node-card-title-lines: 2;
+ --adv-node-card-text-lines: 3;
position: relative;
z-index: 500;
color: #2d241d;
@@ -19,18 +41,36 @@
.adv-megamenu__desktop {
position: relative;
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.6rem;
width: auto;
max-width: 100%;
color: #2d241d;
overflow: visible;
}
+.adv-megamenu__brand {
+ display: inline-flex;
+ flex: 0 0 auto;
+ align-items: center;
+ justify-content: center;
+ text-decoration: none;
+}
+
+.adv-megamenu__brand img {
+ display: block;
+ width: auto;
+ height: auto;
+ max-width: 50px;
+ max-height: 50px;
+}
+
.adv-megamenu__root-list {
width: auto;
display: flex;
align-items: stretch;
- gap: 0.25rem;
+ gap: var(--adv-root-gap);
flex-wrap: nowrap;
white-space: nowrap;
padding: 0 0.25rem;
@@ -45,11 +85,12 @@
.adv-megamenu .adv-megamenu__root-link:visited {
display: flex;
align-items: center;
- gap: 0.55rem;
- min-height: 58px;
- padding: 0 1rem;
+ gap: var(--adv-root-link-gap);
+ min-height: var(--adv-root-link-min-height);
+ padding: 0 var(--adv-root-link-padding-x);
color: #2d241d;
font-weight: 600;
+ font-size: var(--adv-root-font-size);
text-decoration: none;
background: transparent;
}
@@ -99,9 +140,11 @@
.adv-megamenu__panel-inner {
display: grid;
- grid-template-columns: minmax(280px, 320px) 1fr;
- gap: 2rem;
- padding: 1.5rem;
+ grid-template-columns: minmax(var(--adv-panel-nav-min), var(--adv-panel-nav-max)) 1fr;
+ gap: var(--adv-panel-inner-gap);
+ padding: var(--adv-panel-inner-padding);
+ max-height: calc(100vh - 110px);
+ overflow-y: auto;
background: #fffdfa;
color: #2d241d;
border: 1px solid #ddd3c3;
@@ -165,7 +208,7 @@
.adv-megamenu__submenu-title {
margin: 0 0 1rem;
- font-size: 1.1rem;
+ font-size: var(--adv-submenu-title-size);
font-weight: 700;
color: #241a13;
}
@@ -173,16 +216,16 @@
.adv-megamenu__rich-intro {
display: flex;
align-items: flex-start;
- gap: 1.25rem;
+ gap: var(--adv-rich-intro-gap);
margin-bottom: 1.5rem;
- padding: 1.25rem;
+ padding: var(--adv-rich-intro-padding);
border-radius: 18px;
background: #f7f1ea;
}
.adv-megamenu__rich-intro-media {
- flex: 0 0 220px;
- max-width: 220px;
+ flex: 0 0 var(--adv-rich-intro-media-width);
+ max-width: var(--adv-rich-intro-media-width);
}
.adv-megamenu__rich-intro-media img {
@@ -202,12 +245,13 @@
.adv-megamenu__rich-intro-title {
margin: 0;
- font-size: 1.35rem;
+ font-size: var(--adv-rich-intro-title-size);
font-weight: 700;
color: #241a13;
}
.adv-megamenu__rich-intro-description {
+ font-size: var(--adv-card-text-size);
color: #5a4b3f;
line-height: 1.6;
}
@@ -226,6 +270,80 @@
text-decoration: none;
}
+.adv-megamenu__rich-node-row {
+ display: grid;
+ gap: 0.85rem;
+ margin-bottom: 1rem;
+}
+
+.adv-megamenu__rich-node-card {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ gap: 1rem;
+ padding: 1rem;
+ border: 1px solid #ece4d8;
+ border-radius: 16px;
+ background: #ffffff;
+ box-shadow: 0 8px 20px rgba(45, 36, 29, 0.06);
+}
+
+.adv-megamenu__rich-node-media {
+ flex: 0 0 148px;
+ max-width: 148px;
+}
+
+.adv-megamenu__rich-node-media img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: 12px;
+}
+
+.adv-megamenu__rich-node-body {
+ display: flex;
+ flex: 1 1 auto;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.55rem;
+ min-width: 0;
+}
+
+.adv-megamenu__rich-node-title {
+ margin: 0;
+ font-size: 1rem;
+ font-weight: 700;
+ line-height: 1.3;
+}
+
+.adv-megamenu .adv-megamenu__rich-node-title a,
+.adv-megamenu .adv-megamenu__rich-node-title a:link,
+.adv-megamenu .adv-megamenu__rich-node-title a:visited {
+ color: #241a13;
+ text-decoration: none;
+}
+
+.adv-megamenu .adv-megamenu__rich-node-title a:hover {
+ color: #8a3c1f;
+}
+
+.adv-megamenu__rich-node-description {
+ color: #5a4b3f;
+ font-size: 0.92rem;
+ line-height: 1.55;
+}
+
+.adv-megamenu .adv-megamenu__rich-node-link,
+.adv-megamenu .adv-megamenu__rich-node-link:link,
+.adv-megamenu .adv-megamenu__rich-node-link:visited {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ margin-top: auto;
+ text-decoration: none;
+}
+
.adv-megamenu .adv-megamenu__submenu-pane,
.adv-megamenu .adv-megamenu__submenu-pane a,
.adv-megamenu .adv-megamenu__submenu-pane a:link,
@@ -330,13 +448,14 @@
.adv-megamenu__layout-card .card-title {
margin: 0;
- font-size: 1.1rem;
+ font-size: var(--adv-card-title-size);
font-weight: 700;
color: #241a13;
}
.adv-megamenu__layout-card .card-text {
margin: 0;
+ font-size: var(--adv-card-text-size);
color: #5a4b3f;
line-height: 1.5;
}
@@ -381,7 +500,7 @@
}
.adv-megamenu__product-name {
- font-size: 0.9rem;
+ font-size: var(--adv-product-name-size);
line-height: 1.35;
}
@@ -397,12 +516,11 @@
.adv-megamenu__toggle {
display: inline-flex;
align-items: center;
- gap: 0.5rem;
- padding: 0.8rem 1rem;
+ justify-content: center;
+ padding: 0;
border: 0;
- background: #f3efe8;
- color: #2d241d;
- font-weight: 700;
+ background: transparent;
+ color: inherit;
}
.adv-megamenu__overlay {
@@ -484,6 +602,27 @@
background: #fffdfa;
}
+.adv-megamenu__mobile-link-row--with-image {
+ grid-template-columns: 112px minmax(0, 1fr) auto;
+ column-gap: 0.75rem;
+}
+
+.adv-megamenu__mobile-thumb {
+ width: 112px;
+ height: 112px;
+ overflow: hidden;
+ border-radius: 10px;
+ background: #ffffff;
+ border: 1px solid #ece4d8;
+}
+
+.adv-megamenu__mobile-thumb img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
.adv-megamenu .adv-megamenu__mobile-link-row a,
.adv-megamenu .adv-megamenu__mobile-link-row a:link,
.adv-megamenu .adv-megamenu__mobile-link-row a:visited {
@@ -492,9 +631,28 @@
}
@media (max-width: 991px) {
- .adv-megamenu {
- display: block;
+ #header.header.js-sticky-header {
+ position: sticky;
+ top: 0;
+ z-index: 1600;
width: 100%;
+ background-color: inherit;
+ }
+
+ .header-bottom {
+ box-shadow: 0 8px 18px rgba(17, 17, 17, 0.12);
+ }
+
+ .header-bottom__container {
+ position: relative;
+ z-index: 1;
+ }
+
+ .adv-megamenu {
+ display: inline-flex;
+ width: auto;
+ max-width: none;
+ vertical-align: middle;
}
.adv-megamenu__desktop {
@@ -502,7 +660,48 @@
}
.adv-megamenu__mobile {
- display: block;
+ display: flex;
+ flex: 0 0 auto;
+ width: auto;
+ order: 2;
+ align-items: center;
+ gap: 0.4rem;
+ }
+
+ .adv-megamenu__brand--mobile img {
+ max-width: 42px;
+ max-height: 42px;
+ }
+
+ .header-bottom__row > .search__mobile {
+ order: 3;
+ }
+
+ .header-bottom__row > #_mobile_user_info {
+ order: 4;
+ }
+
+ .header-bottom__row > #_mobile_cart {
+ order: 5;
+ }
+
+ .adv-megamenu__toggle.header-block__action-btn {
+ min-height: 100%;
+ padding: 0.5rem;
+ }
+
+ .header-bottom__row > .adv-megamenu,
+ .header-bottom__row > .search__mobile,
+ .header-bottom__row > #_mobile_user_info,
+ .header-bottom__row > #_mobile_cart {
+ align-self: center;
+ }
+
+ .header-bottom__row > .adv-megamenu .header-block__icon,
+ .header-bottom__row > .search__mobile .header-block__icon,
+ .header-bottom__row > #_mobile_user_info .header-block__icon,
+ .header-bottom__row > #_mobile_cart .header-block__icon {
+ line-height: 1;
}
.adv-megamenu__rich-intro {
@@ -517,20 +716,22 @@
.adv-megamenu__node-card-row {
- row-gap: 1rem;
+ row-gap: 0.5rem;
}
.adv-megamenu__node-card {
height: 100%;
border: 1px solid #ebe2d6;
- box-shadow: 0 10px 24px rgba(45, 36, 29, 0.08);
+ box-shadow: 0 6px 16px rgba(45, 36, 29, 0.07);
background: #fffdfa;
}
.adv-megamenu__node-card .card-img-top {
display: block;
- width: 250px;
- height: 250px;
+ width: var(--adv-node-card-image-size) !important;
+ height: var(--adv-node-card-image-size) !important;
+ min-height: 0 !important;
+ max-height: var(--adv-node-card-image-size) !important;
max-width: 100%;
margin: 0 auto;
object-fit: contain;
@@ -540,7 +741,8 @@
.adv-megamenu__node-card .card-body {
display: flex;
flex-direction: column;
- gap: 0.65rem;
+ gap: var(--adv-node-card-gap);
+ padding: var(--adv-node-card-body-padding) !important;
}
.adv-megamenu__node-card .card-title,
@@ -550,9 +752,14 @@
.adv-megamenu__node-card .card-title {
margin: 0;
- font-size: 1rem;
+ font-size: var(--adv-card-title-size);
font-weight: 700;
text-align: center;
+ line-height: 1.2;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: var(--adv-node-card-title-lines);
+ overflow: hidden;
}
.adv-megamenu .adv-megamenu__node-card .card-title a,
@@ -568,7 +775,13 @@
.adv-megamenu__node-card .card-text {
margin: 0;
+ font-size: var(--adv-card-text-size);
line-height: 1.45;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: var(--adv-node-card-text-lines);
+ overflow: hidden;
+ text-align: center;
}
.adv-megamenu__submenu-pane,
@@ -581,3 +794,128 @@
.adv-megamenu__layout-card .card-text {
white-space: normal;
}
+
+@media (min-width: 992px) and (max-width: 1382px) {
+ .adv-megamenu {
+ --adv-root-gap: clamp(0.12rem, 0.08rem + 0.2vw, 0.25rem);
+ --adv-root-link-gap: clamp(0.35rem, 0.22rem + 0.22vw, 0.55rem);
+ --adv-root-link-min-height: clamp(44px, 41px + 0.7vw, 50px);
+ --adv-root-link-padding-x: clamp(0.45rem, 0.15rem + 0.42vw, 0.7rem);
+ --adv-root-font-size: clamp(0.78rem, 0.6rem + 0.28vw, 0.9rem);
+ --adv-panel-inner-gap: clamp(0.85rem, 0.45rem + 0.45vw, 1.25rem);
+ --adv-panel-inner-padding: clamp(0.75rem, 0.45rem + 0.35vw, 1rem);
+ --adv-panel-nav-min: clamp(210px, 17vw, 240px);
+ --adv-panel-nav-max: clamp(225px, 20vw, 280px);
+ --adv-submenu-title-size: clamp(0.9rem, 0.72rem + 0.2vw, 1rem);
+ --adv-rich-intro-gap: clamp(0.7rem, 0.45rem + 0.25vw, 0.9rem);
+ --adv-rich-intro-padding: clamp(0.75rem, 0.45rem + 0.28vw, 0.95rem);
+ --adv-rich-intro-media-width: clamp(140px, 13vw, 180px);
+ --adv-rich-intro-title-size: clamp(0.98rem, 0.8rem + 0.24vw, 1.12rem);
+ --adv-card-title-size: clamp(0.86rem, 0.72rem + 0.2vw, 0.95rem);
+ --adv-card-text-size: clamp(0.78rem, 0.68rem + 0.16vw, 0.88rem);
+ --adv-product-name-size: clamp(0.74rem, 0.66rem + 0.12vw, 0.8rem);
+ --adv-node-card-image-size: clamp(64px, 6vw, 88px);
+ --adv-node-card-body-padding: clamp(0.2rem, 0.15rem + 0.06vw, 0.3rem);
+ --adv-node-card-gap: clamp(0.12rem, 0.1rem + 0.04vw, 0.2rem);
+ }
+
+ .adv-megamenu__panel {
+ width: var(--adv-panel-width, min(1380px, calc(100vw - 1rem)));
+ min-width: var(--adv-panel-width, min(1380px, calc(100vw - 1rem)));
+ }
+
+ .adv-megamenu__manual-tree li.has-submenu > a {
+ padding-bottom: clamp(0.45rem, 0.25rem + 0.25vw, 0.75rem);
+ padding-right: 1.25rem;
+ }
+
+ .adv-megamenu .adv-megamenu__category-tree a,
+ .adv-megamenu .adv-megamenu__category-tree a:link,
+ .adv-megamenu .adv-megamenu__category-tree a:visited,
+ .adv-megamenu .adv-megamenu__manual-tree a,
+ .adv-megamenu .adv-megamenu__manual-tree a:link,
+ .adv-megamenu .adv-megamenu__manual-tree a:visited,
+ .adv-megamenu .adv-megamenu__panel-tree a,
+ .adv-megamenu .adv-megamenu__panel-tree a:link,
+ .adv-megamenu .adv-megamenu__panel-tree a:visited {
+ font-size: clamp(0.76rem, 0.56rem + 0.22vw, 0.95rem);
+ }
+
+ .adv-megamenu__product-link {
+ padding: clamp(0.5rem, 0.3rem + 0.3vw, 0.75rem);
+ }
+
+ .adv-megamenu__products {
+ gap: clamp(0.45rem, 0.2rem + 0.25vw, 0.75rem);
+ }
+
+ .adv-megamenu__node-card .card-text {
+ display: none;
+ }
+
+ .adv-megamenu__node-card {
+ border-radius: 10px;
+ }
+
+ .adv-megamenu__rich-node-card {
+ gap: 0.8rem;
+ padding: 0.85rem;
+ }
+
+ .adv-megamenu__rich-node-media {
+ flex-basis: 120px;
+ max-width: 120px;
+ }
+
+ .adv-megamenu__rich-node-title {
+ font-size: 0.92rem;
+ }
+
+ .adv-megamenu__rich-node-description {
+ font-size: 0.82rem;
+ }
+}
+
+@media (min-width: 1600px) {
+ .adv-megamenu {
+ --adv-card-title-size: 1rem;
+ --adv-card-text-size: 0.92rem;
+ --adv-root-link-gap: 0.55rem;
+ --adv-root-link-min-height: 58px;
+ --adv-root-link-padding-x: 1rem;
+ --adv-root-font-size: 1rem;
+ --adv-panel-inner-gap: 2rem;
+ --adv-panel-inner-padding: 1.5rem;
+ --adv-panel-nav-min: 280px;
+ --adv-panel-nav-max: 320px;
+ --adv-submenu-title-size: 1.1rem;
+ --adv-rich-intro-gap: 1.25rem;
+ --adv-rich-intro-padding: 1.25rem;
+ --adv-rich-intro-media-width: 220px;
+ --adv-rich-intro-title-size: 1.35rem;
+ --adv-product-name-size: 0.9rem;
+ --adv-node-card-image-size: 150px;
+ --adv-node-card-body-padding: 0.55rem;
+ --adv-node-card-gap: 0.35rem;
+ }
+
+ .adv-megamenu__node-card .card-text {
+ display: none;
+ }
+
+ .adv-megamenu__rich-node-media {
+ flex-basis: 150px;
+ max-width: 150px;
+ }
+}
+
+@media (max-width: 991px) {
+ .adv-megamenu__rich-node-card {
+ flex-direction: column;
+ }
+
+ .adv-megamenu__rich-node-media {
+ flex-basis: auto;
+ max-width: 100%;
+ }
+}
diff --git a/views/js/admin.js b/views/js/admin.js
index 23ccf09..5387f9a 100644
--- a/views/js/admin.js
+++ b/views/js/admin.js
@@ -1,421 +1,794 @@
(function () {
function boot() {
- var root = document.getElementById('adv-megamenu-admin');
- if (!root) {
- return;
- }
-
- var treeContainer = root.querySelector('.js-tree');
- var treeInput = document.getElementById('advmegamenu_tree_json');
- var ajaxUrl = root.getAttribute('data-ajax-url');
- var token = root.getAttribute('data-token');
- var languages = JSON.parse((root.querySelector('.js-adv-languages-data') || {}).textContent || '[]');
- var tree = JSON.parse((root.querySelector('.js-adv-tree-data') || {}).textContent || '[]');
- var tempId = Date.now();
-
- function nextId() {
- tempId += 1;
- return tempId;
- }
-
- function createEmptyNode() {
- var lang = {};
- languages.forEach(function (language) {
- lang[language.id_lang] = {
- title: '',
- description: '',
- custom_link: ''
- };
- });
-
- return {
- id: nextId(),
- active: true,
- type: 'category',
- entity_id: 0,
- icon_path: '',
- icon_url: '',
- new_window: false,
- lang: lang,
- layouts: [],
- children: []
- };
- }
-
- function createEmptyLayout() {
- return {
- id: nextId(),
- column_width: 4,
- show_title: true,
- custom_image: '',
- custom_image_url: '',
- background_color: '',
- block_type: 'promo',
- products: []
- };
- }
-
- function serializeTree() {
- treeInput.value = JSON.stringify(tree);
- }
-
- function render() {
- serializeTree();
- treeContainer.innerHTML = '';
- tree.forEach(function (node, index) {
- treeContainer.appendChild(renderNode(node, tree, index, 0));
- });
- }
-
- function move(collection, index, direction) {
- var target = direction === 'up' ? index - 1 : index + 1;
- if (target < 0 || target >= collection.length) {
+ var root = document.getElementById('adv-megamenu-admin');
+ if (!root) {
return;
}
- var current = collection[index];
- collection[index] = collection[target];
- collection[target] = current;
- render();
- }
+ var treeContainer = root.querySelector('.js-tree');
+ var treeInput = document.getElementById('advmegamenu_tree_json');
+ var ajaxUrl = root.getAttribute('data-ajax-url');
+ var token = root.getAttribute('data-token');
+ var languages = parseJson(root.querySelector('.js-adv-languages-data'), []);
+ var labels = parseJson(root.querySelector('.js-adv-i18n'), {});
+ var tree = parseJson(root.querySelector('.js-adv-tree-data'), []);
+ var tempId = Date.now();
+ var nodeModal = document.getElementById('advNodeModal');
+ var layoutModal = document.getElementById('advLayoutModal');
+ var confirmModal = document.getElementById('advConfirmModal');
+ var nodeModalState = null;
+ var layoutModalState = null;
+ var confirmAction = null;
+ var activeRootId = tree.length ? tree[0].id : null;
- function remove(collection, index) {
- collection.splice(index, 1);
- render();
- }
+ function nextId() {
+ tempId += 1;
+ return tempId;
+ }
- function renderNode(node, collection, index, depth) {
- var wrapper = document.createElement('section');
- wrapper.className = 'adv-menu-node';
-
- var title = getNodeTitle(node);
- wrapper.innerHTML = '' +
- '
' +
- '' +
- ' ' +
- ' ' +
- ' ' +
- ' ' +
- '
';
-
- wrapper.querySelector('.js-node-type').value = node.type || 'category';
- wrapper.querySelector('.js-entity-id').value = node.entity_id || 0;
- wrapper.querySelector('.js-active').checked = !!node.active;
- wrapper.querySelector('.js-new-window').checked = !!node.new_window;
- wrapper.querySelector('.js-icon-path').value = node.icon_path || '';
-
- var iconPreviewWrap = wrapper.querySelector('.js-icon-preview-wrap');
- renderImagePreview(iconPreviewWrap, node.icon_url);
-
- wrapper.querySelector('.js-node-type').addEventListener('change', function (event) {
- node.type = event.target.value;
- render();
- });
- wrapper.querySelector('.js-entity-id').addEventListener('input', function (event) {
- node.entity_id = parseInt(event.target.value || '0', 10);
- serializeTree();
- });
- wrapper.querySelector('.js-active').addEventListener('change', function (event) {
- node.active = event.target.checked;
- serializeTree();
- });
- wrapper.querySelector('.js-new-window').addEventListener('change', function (event) {
- node.new_window = event.target.checked;
- serializeTree();
- });
-
- wrapper.querySelector('.js-icon-upload').addEventListener('change', function (event) {
- uploadImage(event.target.files[0], 'menu_icon', function (response) {
- node.icon_path = response.path;
- node.icon_url = response.url;
- serializeTree();
- renderImagePreview(iconPreviewWrap, response.url);
- });
- });
-
- wrapper.querySelector('.js-move-up').addEventListener('click', function () {
- move(collection, index, 'up');
- });
- wrapper.querySelector('.js-move-down').addEventListener('click', function () {
- move(collection, index, 'down');
- });
- wrapper.querySelector('.js-remove-node').addEventListener('click', function () {
- remove(collection, index);
- });
- wrapper.querySelector('.js-add-child').addEventListener('click', function () {
- node.children = node.children || [];
- node.children.push(createEmptyNode());
- render();
- });
- wrapper.querySelector('.js-add-layout').addEventListener('click', function () {
- node.layouts = node.layouts || [];
- node.layouts.push(createEmptyLayout());
- render();
- });
-
- var langWrap = wrapper.querySelector('.adv-menu-node__languages');
- languages.forEach(function (language) {
- var langId = String(language.id_lang);
- if (!node.lang[langId]) {
- node.lang[langId] = { title: '', description: '', custom_link: '' };
+ function parseJson(element, fallback) {
+ if (!element || !element.textContent) {
+ return fallback;
}
- var box = document.createElement('div');
- box.className = 'adv-menu-language';
- box.innerHTML = '' +
- '' + escapeHtml(language.iso_code + ' / ' + language.name) + '
' +
- '' +
- '' +
- '';
+ try {
+ return JSON.parse(element.textContent);
+ } catch (error) {
+ return fallback;
+ }
+ }
- box.querySelector('.js-lang-title').value = node.lang[langId].title || '';
- box.querySelector('.js-lang-description').value = node.lang[langId].description || '';
- box.querySelector('.js-lang-link').value = node.lang[langId].custom_link || '';
- box.querySelector('.js-lang-title').addEventListener('input', function (event) {
- node.lang[langId].title = event.target.value;
- serializeTree();
- });
- box.querySelector('.js-lang-description').addEventListener('input', function (event) {
- node.lang[langId].description = event.target.value;
- serializeTree();
- });
- box.querySelector('.js-lang-link').addEventListener('input', function (event) {
- node.lang[langId].custom_link = event.target.value;
- serializeTree();
+ function t(key, fallback) {
+ return labels[key] || fallback || key;
+ }
+
+ function showModal(element) {
+ if (window.jQuery && window.jQuery.fn && window.jQuery.fn.modal) {
+ window.jQuery(element).modal('show');
+ }
+ }
+
+ function hideModal(element) {
+ if (window.jQuery && window.jQuery.fn && window.jQuery.fn.modal) {
+ window.jQuery(element).modal('hide');
+ }
+ }
+
+ function createEmptyNode() {
+ var lang = {};
+ languages.forEach(function (language) {
+ lang[String(language.id_lang)] = {
+ title: '',
+ description: '',
+ custom_link: ''
+ };
});
- langWrap.appendChild(box);
- });
+ return {
+ id: nextId(),
+ active: true,
+ type: 'link',
+ entity_id: 0,
+ icon_path: '',
+ icon_url: '',
+ new_window: false,
+ lang: lang,
+ layouts: [],
+ children: []
+ };
+ }
- var layoutsWrap = wrapper.querySelector('.adv-menu-node__layouts');
- (node.layouts || []).forEach(function (layout, layoutIndex) {
- layoutsWrap.appendChild(renderLayout(node.layouts, layout, layoutIndex));
- });
+ function createEmptyLayout() {
+ return {
+ id: nextId(),
+ column_width: 4,
+ show_title: true,
+ custom_image: '',
+ custom_image_url: '',
+ background_color: '',
+ block_type: 'promo',
+ products: []
+ };
+ }
- var childrenWrap = wrapper.querySelector('.adv-menu-node__children');
- (node.children || []).forEach(function (child, childIndex) {
- childrenWrap.appendChild(renderNode(child, node.children, childIndex, depth + 1));
- });
-
- return wrapper;
- }
-
- function renderLayout(layouts, layout, index) {
- var wrapper = document.createElement('section');
- wrapper.className = 'adv-menu-layout';
- wrapper.innerHTML = '' +
- '' +
- '' +
- ' ' +
- ' ' +
- ' ' +
- ' ' +
- '
';
-
- wrapper.querySelector('.js-layout-width').value = layout.column_width || 4;
- wrapper.querySelector('.js-layout-type').value = layout.block_type || 'promo';
- wrapper.querySelector('.js-layout-title').checked = !!layout.show_title;
- wrapper.querySelector('.js-layout-bg').value = layout.background_color || '';
-
- var previewWrap = wrapper.querySelector('.js-layout-preview-wrap');
- renderImagePreview(previewWrap, layout.custom_image_url);
-
- wrapper.querySelector('.js-layout-width').addEventListener('input', function (event) {
- layout.column_width = parseInt(event.target.value || '4', 10);
- serializeTree();
- });
- wrapper.querySelector('.js-layout-type').addEventListener('change', function (event) {
- layout.block_type = event.target.value;
- serializeTree();
- });
- wrapper.querySelector('.js-layout-title').addEventListener('change', function (event) {
- layout.show_title = event.target.checked;
- serializeTree();
- });
- wrapper.querySelector('.js-layout-bg').addEventListener('input', function (event) {
- layout.background_color = event.target.value;
- serializeTree();
- });
- wrapper.querySelector('.js-layout-upload').addEventListener('change', function (event) {
- uploadImage(event.target.files[0], 'layout_image', function (response) {
- layout.custom_image = response.path;
- layout.custom_image_url = response.url;
- serializeTree();
- renderImagePreview(previewWrap, response.url);
+ function ensureNodeDefaults(node) {
+ if (!node.lang) {
+ node.lang = {};
+ }
+ if (!node.layouts) {
+ node.layouts = [];
+ }
+ if (!node.children) {
+ node.children = [];
+ }
+ languages.forEach(function (language) {
+ var langId = String(language.id_lang);
+ if (!node.lang[langId]) {
+ node.lang[langId] = {
+ title: '',
+ description: '',
+ custom_link: ''
+ };
+ }
});
- });
+ }
- wrapper.querySelector('.js-layout-up').addEventListener('click', function () {
- move(layouts, index, 'up');
- });
- wrapper.querySelector('.js-layout-down').addEventListener('click', function () {
- move(layouts, index, 'down');
- });
- wrapper.querySelector('.js-remove-layout').addEventListener('click', function () {
- remove(layouts, index);
- });
+ function sanitizeTree(nodes, depth) {
+ nodes.forEach(function (node) {
+ ensureNodeDefaults(node);
- var productsWrap = wrapper.querySelector('.js-products');
- renderProducts(layout.products || [], productsWrap, function (nextProducts) {
- layout.products = nextProducts;
+ if (depth === 0) {
+ node.icon_path = '';
+ node.icon_url = '';
+ Object.keys(node.lang).forEach(function (langId) {
+ node.lang[langId].description = '';
+ });
+ }
+
+ sanitizeTree(node.children || [], depth + 1);
+ });
+ }
+
+ function serializeTree() {
+ sanitizeTree(tree, 0);
+ treeInput.value = JSON.stringify(tree);
+ }
+
+ function render() {
serializeTree();
- });
+ treeContainer.innerHTML = '';
- var resultsWrap = wrapper.querySelector('.js-product-results');
- var searchInput = wrapper.querySelector('.js-product-search');
- var searchTimer = null;
- searchInput.addEventListener('input', function () {
- clearTimeout(searchTimer);
- var value = searchInput.value.trim();
- if (value.length < 2) {
- resultsWrap.innerHTML = '';
+ if (!tree.length) {
+ var empty = document.createElement('div');
+ empty.className = 'adv-megamenu-admin__empty';
+ empty.textContent = t('emptyTree', 'No menu items yet.');
+ treeContainer.appendChild(empty);
return;
}
- searchTimer = window.setTimeout(function () {
- fetch(ajaxUrl + '&ajax=1&action=searchProducts&q=' + encodeURIComponent(value))
- .then(function (response) { return response.json(); })
- .then(function (payload) {
- resultsWrap.innerHTML = '';
- (payload.results || []).forEach(function (result) {
- var button = document.createElement('button');
- button.type = 'button';
- button.textContent = result.label;
- button.addEventListener('click', function () {
- layout.products = layout.products || [];
- if (layout.products.indexOf(result.id) === -1) {
- layout.products.push(result.id);
- }
- render();
- });
- resultsWrap.appendChild(button);
- });
- });
- }, 200);
- });
+ if (!findRootById(activeRootId)) {
+ activeRootId = tree[0].id;
+ }
- return wrapper;
- }
+ var shell = document.createElement('div');
+ shell.className = 'adv-menu-admin-root-shell';
+ shell.innerHTML = '' +
+ '' +
+ '';
- function renderProducts(products, wrap, onChange) {
- wrap.innerHTML = '';
- products.forEach(function (productId, index) {
- var chip = document.createElement('span');
- chip.className = 'adv-menu-product-chip';
- chip.innerHTML = '#' + escapeHtml(String(productId)) + '';
- chip.querySelector('button').addEventListener('click', function () {
- var nextProducts = products.slice();
- nextProducts.splice(index, 1);
- onChange(nextProducts);
+ var tabs = shell.querySelector('.adv-menu-admin-root-tabs');
+ var panel = shell.querySelector('.adv-menu-admin-root-panel');
+
+ tree.forEach(function (node, index) {
+ tabs.appendChild(renderRootTab(node, tree, index));
+ });
+
+ var activeRoot = findRootById(activeRootId) || tree[0];
+ if (activeRoot) {
+ panel.appendChild(renderNode(activeRoot, tree, getRootIndex(activeRoot.id), 0));
+ }
+
+ treeContainer.appendChild(shell);
+ }
+
+ function renderRootTab(node, collection, index) {
+ var button = document.createElement('button');
+ button.type = 'button';
+ button.className = 'adv-menu-admin-root-tab' + (node.id === activeRootId ? ' is-active' : '');
+ button.innerHTML = '' +
+ '' +
+ ' ' +
+ ' ' +
+ '' +
+ '';
+
+ button.addEventListener('click', function () {
+ activeRootId = node.id;
render();
});
- wrap.appendChild(chip);
- });
- }
- function renderImagePreview(wrap, url) {
- wrap.innerHTML = '';
- if (!url) {
- return;
+ button.querySelector('.js-root-edit').addEventListener('click', function (event) {
+ event.stopPropagation();
+ openNodeModal(node, 0);
+ });
+ button.querySelector('.js-root-up').addEventListener('click', function (event) {
+ event.stopPropagation();
+ move(collection, index, 'up');
+ });
+ button.querySelector('.js-root-down').addEventListener('click', function (event) {
+ event.stopPropagation();
+ move(collection, index, 'down');
+ });
+ button.querySelector('.js-root-delete').addEventListener('click', function (event) {
+ event.stopPropagation();
+ askConfirm(t('deleteNodeTitle', 'Delete menu item'), t('deleteNodeText', 'This will remove the selected menu item together with its children and attached blocks.'), function () {
+ collection.splice(index, 1);
+ if (activeRootId === node.id) {
+ activeRootId = collection.length ? collection[Math.max(0, index - 1)].id : null;
+ }
+ render();
+ });
+ });
+
+ return button;
}
- var image = document.createElement('img');
- image.src = url;
- image.className = 'adv-menu-image-preview';
- wrap.appendChild(image);
- }
+ function renderNode(node, collection, index, depth) {
+ ensureNodeDefaults(node);
- function getNodeTitle(node) {
- var firstLang = languages[0] ? String(languages[0].id_lang) : null;
- if (firstLang && node.lang[firstLang] && node.lang[firstLang].title) {
- return node.lang[firstLang].title;
+ var wrapper = document.createElement('section');
+ wrapper.className = 'adv-menu-admin-node' + (depth === 0 ? ' adv-menu-admin-node--root' : '') + (depth >= 2 ? ' adv-menu-admin-node--compact' : '');
+
+ var title = getNodeTitle(node) || t(depth === 0 ? 'rootItem' : 'childItem', 'Menu item');
+ var customLink = getNodeLink(node);
+ var imageHtml = depth > 0 && node.icon_url ? '' : '';
+ var meta = [];
+
+ meta.push(metaItem(node.type || 'link', 'icon-tag'));
+ if (!node.active) {
+ meta.push(metaItem(t('inactive', 'Inactive'), 'icon-ban-circle'));
+ }
+ if (node.new_window) {
+ meta.push(metaItem(t('newWindow', 'New window'), 'icon-external-link'));
+ }
+ meta.push(metaItem(String((node.children || []).length) + ' ' + t('children', 'children'), 'icon-sitemap'));
+ meta.push(metaItem(String((node.layouts || []).length) + ' ' + t('blocks', 'blocks'), 'icon-th-large'));
+
+ wrapper.innerHTML = '' +
+ '' +
+ '' +
+ ' ' +
+ ' ' +
+ '
';
+
+ bindNodeActions(wrapper, node, collection, index, depth);
+
+ var layoutsWrap = wrapper.querySelector('.adv-menu-admin-node__layouts-wrap');
+ if ((node.layouts || []).length) {
+ var layoutsTitle = document.createElement('h5');
+ layoutsTitle.className = 'adv-menu-admin-node__section-title';
+ layoutsTitle.innerHTML = '' + escapeHtml(t('blocks', 'blocks')) + '';
+ layoutsWrap.appendChild(layoutsTitle);
+
+ var layoutList = document.createElement('div');
+ layoutList.className = 'adv-menu-admin-layout-list';
+ node.layouts.forEach(function (layout, layoutIndex) {
+ layoutList.appendChild(renderLayout(node.layouts, layout, layoutIndex));
+ });
+ layoutsWrap.appendChild(layoutList);
+ }
+
+ var childrenWrap = wrapper.querySelector('.adv-menu-admin-node__children-wrap');
+ if ((node.children || []).length) {
+ var childrenTitle = document.createElement('h5');
+ childrenTitle.className = 'adv-menu-admin-node__section-title';
+ childrenTitle.innerHTML = '' + escapeHtml(t('children', 'children')) + '';
+ childrenWrap.appendChild(childrenTitle);
+
+ var childList = document.createElement('div');
+ childList.className = 'adv-menu-admin-children-list adv-menu-admin-node__children' + (depth >= 1 ? ' adv-menu-admin-node__children--grid' : '');
+ node.children.forEach(function (child, childIndex) {
+ childList.appendChild(renderNode(child, node.children, childIndex, depth + 1));
+ });
+ childrenWrap.appendChild(childList);
+ }
+
+ return wrapper;
}
- var langIds = Object.keys(node.lang || {});
- if (langIds.length && node.lang[langIds[0]]) {
- return node.lang[langIds[0]].title || '';
+ function renderLayout(layouts, layout, index) {
+ var wrapper = document.createElement('section');
+ wrapper.className = 'adv-menu-admin-layout';
+
+ var meta = [];
+ meta.push(metaItem('col-' + (layout.column_width || 4), 'icon-columns'));
+ meta.push(metaItem(layout.block_type || 'promo', 'icon-th'));
+ if (layout.show_title) {
+ meta.push(metaItem(t('showTitle', 'Show title'), 'icon-font'));
+ }
+ if ((layout.products || []).length) {
+ meta.push(metaItem(String(layout.products.length) + ' ' + t('products', 'Products'), 'icon-shopping-cart'));
+ }
+
+ wrapper.innerHTML = '' +
+ '' +
+ '' +
+ (layout.custom_image_url ? '' : '') +
+ '
';
+
+ wrapper.querySelector('.js-layout-up').addEventListener('click', function () {
+ move(layouts, index, 'up');
+ });
+ wrapper.querySelector('.js-layout-down').addEventListener('click', function () {
+ move(layouts, index, 'down');
+ });
+ wrapper.querySelector('.js-layout-edit').addEventListener('click', function () {
+ openLayoutModal(layout);
+ });
+ wrapper.querySelector('.js-layout-delete').addEventListener('click', function () {
+ askConfirm(t('deleteLayoutTitle', 'Delete rich block'), t('deleteLayoutText', 'This will remove the selected rich content block.'), function () {
+ layouts.splice(index, 1);
+ render();
+ });
+ });
+
+ return wrapper;
}
- return '';
- }
-
- function uploadImage(file, preset, callback) {
- if (!file) {
- return;
+ function bindNodeActions(wrapper, node, collection, index, depth) {
+ wrapper.querySelector('.js-node-up').addEventListener('click', function () {
+ move(collection, index, 'up');
+ });
+ wrapper.querySelector('.js-node-down').addEventListener('click', function () {
+ move(collection, index, 'down');
+ });
+ wrapper.querySelector('.js-node-edit').addEventListener('click', function () {
+ openNodeModal(node, depth);
+ });
+ wrapper.querySelector('.js-node-add-child').addEventListener('click', function () {
+ var child = createEmptyNode();
+ openNodeModal(child, depth + 1, {
+ onSave: function (savedChild) {
+ node.children.push(savedChild);
+ }
+ });
+ });
+ wrapper.querySelector('.js-node-add-layout').addEventListener('click', function () {
+ var layout = createEmptyLayout();
+ openLayoutModal(layout, {
+ onSave: function (savedLayout) {
+ node.layouts.push(savedLayout);
+ }
+ });
+ });
+ wrapper.querySelector('.js-node-delete').addEventListener('click', function () {
+ askConfirm(t('deleteNodeTitle', 'Delete menu item'), t('deleteNodeText', 'This will remove the selected menu item together with its children and attached blocks.'), function () {
+ collection.splice(index, 1);
+ render();
+ });
+ });
}
- var formData = new FormData();
- formData.append('ajax', '1');
- formData.append('action', 'uploadImage');
- formData.append('token', token);
- formData.append('preset', preset || 'default');
- formData.append('image', file);
+ function actionButton(className, icon, label) {
+ return '' +
+ '';
+ }
- fetch(ajaxUrl, {
- method: 'POST',
- body: formData
- })
- .then(function (response) { return response.json(); })
- .then(function (payload) {
- if (payload && !payload.error) {
- callback(payload);
+ function metaItem(text, icon) {
+ return '';
+ }
+
+ function move(collection, index, direction) {
+ var target = direction === 'up' ? index - 1 : index + 1;
+ if (target < 0 || target >= collection.length) {
+ return;
+ }
+
+ var current = collection[index];
+ collection[index] = collection[target];
+ collection[target] = current;
+ if (collection === tree && current.id === activeRootId) {
+ activeRootId = current.id;
+ }
+ render();
+ }
+
+ function findRootById(id) {
+ for (var i = 0; i < tree.length; i += 1) {
+ if (tree[i].id === id) {
+ return tree[i];
+ }
+ }
+
+ return null;
+ }
+
+ function getRootIndex(id) {
+ for (var i = 0; i < tree.length; i += 1) {
+ if (tree[i].id === id) {
+ return i;
+ }
+ }
+
+ return 0;
+ }
+
+ function openNodeModal(node, depth, options) {
+ nodeModalState = {
+ node: node,
+ depth: depth,
+ onSave: options && typeof options.onSave === 'function' ? options.onSave : null
+ };
+ ensureNodeDefaults(node);
+
+ nodeModal.querySelector('.js-node-modal-title').textContent = t('nodeModalTitle', 'Edit menu item');
+ nodeModal.querySelector('.js-node-type').value = node.type || 'link';
+ nodeModal.querySelector('.js-node-entity-id').value = node.entity_id || 0;
+ nodeModal.querySelector('.js-node-active').checked = !!node.active;
+ nodeModal.querySelector('.js-node-new-window').checked = !!node.new_window;
+
+ var rootHint = nodeModal.querySelector('.js-node-root-hint');
+ var imageGroup = nodeModal.querySelector('.js-node-image-group');
+ var previewWrap = nodeModal.querySelector('.js-node-icon-preview');
+ renderImagePreview(previewWrap, depth > 0 ? node.icon_url : '');
+
+ rootHint.style.display = depth === 0 ? 'block' : 'none';
+ rootHint.textContent = t('rootOnlyHint', 'For first-level items only title and custom link are edited here.');
+ imageGroup.style.display = depth === 0 ? 'none' : 'block';
+
+ renderNodeLanguagePanels(node, depth);
+
+ var uploadInput = nodeModal.querySelector('.js-node-icon-upload');
+ uploadInput.value = '';
+ uploadInput.onchange = function (event) {
+ uploadImage(event.target.files[0], 'menu_icon', function (response) {
+ node.icon_path = response.path;
+ node.icon_url = response.url;
+ renderImagePreview(previewWrap, response.url);
+ serializeTree();
+ });
+ };
+
+ showModal(nodeModal);
+ }
+
+ function renderNodeLanguagePanels(node, depth) {
+ var wrap = nodeModal.querySelector('.js-node-languages');
+ wrap.innerHTML = '';
+
+ languages.forEach(function (language) {
+ var langId = String(language.id_lang);
+ var lang = node.lang[langId] || { title: '', description: '', custom_link: '' };
+ var panel = document.createElement('div');
+ panel.className = 'adv-megamenu-admin__lang-panel';
+ panel.innerHTML = '' +
+ '' + escapeHtml(language.iso_code + ' / ' + language.name) + '
' +
+ '';
+
+ panel.querySelector('.js-lang-title').addEventListener('input', function (event) {
+ node.lang[langId].title = event.target.value;
+ serializeTree();
+ });
+ if (depth > 0) {
+ panel.querySelector('.js-lang-description').addEventListener('input', function (event) {
+ node.lang[langId].description = event.target.value;
+ serializeTree();
+ });
+ }
+ panel.querySelector('.js-lang-link').addEventListener('input', function (event) {
+ node.lang[langId].custom_link = event.target.value;
+ serializeTree();
+ });
+
+ wrap.appendChild(panel);
+ });
+ }
+
+ function saveNodeModal() {
+ if (!nodeModalState) {
+ return;
+ }
+
+ var node = nodeModalState.node;
+ var depth = nodeModalState.depth;
+ var onSave = nodeModalState.onSave;
+ node.type = nodeModal.querySelector('.js-node-type').value;
+ node.entity_id = parseInt(nodeModal.querySelector('.js-node-entity-id').value || '0', 10);
+ node.active = nodeModal.querySelector('.js-node-active').checked;
+ node.new_window = nodeModal.querySelector('.js-node-new-window').checked;
+
+ if (depth === 0) {
+ node.icon_path = '';
+ node.icon_url = '';
+ Object.keys(node.lang).forEach(function (langId) {
+ node.lang[langId].description = '';
+ });
+ }
+
+ if (onSave) {
+ onSave(node);
+ }
+
+ hideModal(nodeModal);
+ nodeModalState = null;
+ render();
+ }
+
+ function openLayoutModal(layout, options) {
+ layoutModalState = {
+ layout: layout,
+ onSave: options && typeof options.onSave === 'function' ? options.onSave : null
+ };
+ layout.products = layout.products || [];
+
+ layoutModal.querySelector('.js-layout-width').value = layout.column_width || 4;
+ layoutModal.querySelector('.js-layout-type').value = layout.block_type || 'promo';
+ layoutModal.querySelector('.js-layout-title').checked = !!layout.show_title;
+ layoutModal.querySelector('.js-layout-bg').value = layout.background_color || '';
+ renderImagePreview(layoutModal.querySelector('.js-layout-preview'), layout.custom_image_url);
+ renderProducts(layout.products, layoutModal.querySelector('.js-layout-products'));
+ layoutModal.querySelector('.js-layout-product-results').innerHTML = '';
+ layoutModal.querySelector('.js-layout-product-search').value = '';
+
+ var uploadInput = layoutModal.querySelector('.js-layout-upload');
+ uploadInput.value = '';
+ uploadInput.onchange = function (event) {
+ uploadImage(event.target.files[0], 'layout_image', function (response) {
+ layout.custom_image = response.path;
+ layout.custom_image_url = response.url;
+ renderImagePreview(layoutModal.querySelector('.js-layout-preview'), response.url);
+ serializeTree();
+ });
+ };
+
+ bindLayoutProductSearch(layout);
+ showModal(layoutModal);
+ }
+
+ function bindLayoutProductSearch(layout) {
+ var resultsWrap = layoutModal.querySelector('.js-layout-product-results');
+ var searchInput = layoutModal.querySelector('.js-layout-product-search');
+ var searchTimer = null;
+
+ searchInput.oninput = function () {
+ clearTimeout(searchTimer);
+ var value = searchInput.value.trim();
+ if (value.length < 2) {
+ resultsWrap.innerHTML = '';
+ return;
+ }
+
+ searchTimer = window.setTimeout(function () {
+ fetch(ajaxUrl + '&ajax=1&action=searchProducts&q=' + encodeURIComponent(value))
+ .then(function (response) {
+ return response.json();
+ })
+ .then(function (payload) {
+ resultsWrap.innerHTML = '';
+ (payload.results || []).forEach(function (result) {
+ var button = document.createElement('button');
+ button.type = 'button';
+ button.textContent = result.label;
+ button.addEventListener('click', function () {
+ if (layout.products.indexOf(result.id) === -1) {
+ layout.products.push(result.id);
+ }
+ renderProducts(layout.products, layoutModal.querySelector('.js-layout-products'));
+ serializeTree();
+ });
+ resultsWrap.appendChild(button);
+ });
+ });
+ }, 200);
+ };
+ }
+
+ function renderProducts(products, wrap) {
+ wrap.innerHTML = '';
+ if (!products.length) {
+ wrap.innerHTML = '' + escapeHtml(t('noProducts', 'No products attached.')) + '';
+ return;
+ }
+
+ products.forEach(function (productId, index) {
+ var chip = document.createElement('span');
+ chip.className = 'adv-menu-product-chip';
+ chip.innerHTML = '#' + escapeHtml(String(productId)) + '';
+ chip.querySelector('button').addEventListener('click', function () {
+ products.splice(index, 1);
+ renderProducts(products, wrap);
+ serializeTree();
+ });
+ wrap.appendChild(chip);
+ });
+ }
+
+ function saveLayoutModal() {
+ if (!layoutModalState) {
+ return;
+ }
+
+ var layout = layoutModalState.layout;
+ var onSave = layoutModalState.onSave;
+ layout.column_width = parseInt(layoutModal.querySelector('.js-layout-width').value || '4', 10);
+ layout.block_type = layoutModal.querySelector('.js-layout-type').value;
+ layout.show_title = layoutModal.querySelector('.js-layout-title').checked;
+ layout.background_color = layoutModal.querySelector('.js-layout-bg').value;
+
+ if (onSave) {
+ onSave(layout);
+ }
+
+ hideModal(layoutModal);
+ layoutModalState = null;
+ render();
+ }
+
+ function askConfirm(title, text, callback) {
+ confirmAction = callback;
+ confirmModal.querySelector('.js-confirm-title').textContent = title;
+ confirmModal.querySelector('.js-confirm-text').textContent = text;
+ showModal(confirmModal);
+ }
+
+ function renderImagePreview(wrap, url) {
+ wrap.innerHTML = '';
+ if (!url) {
+ return;
+ }
+
+ var image = document.createElement('img');
+ image.src = url;
+ image.className = 'adv-menu-image-preview';
+ wrap.appendChild(image);
+ }
+
+ function getNodeTitle(node) {
+ var defaultLang = languages[0] ? String(languages[0].id_lang) : null;
+ if (defaultLang && node.lang[defaultLang] && node.lang[defaultLang].title) {
+ return node.lang[defaultLang].title;
+ }
+
+ var langIds = Object.keys(node.lang || {});
+ for (var i = 0; i < langIds.length; i += 1) {
+ if (node.lang[langIds[i]] && node.lang[langIds[i]].title) {
+ return node.lang[langIds[i]].title;
+ }
+ }
+
+ return '';
+ }
+
+ function getNodeLink(node) {
+ var defaultLang = languages[0] ? String(languages[0].id_lang) : null;
+ if (defaultLang && node.lang[defaultLang] && node.lang[defaultLang].custom_link) {
+ return node.lang[defaultLang].custom_link;
+ }
+
+ var langIds = Object.keys(node.lang || {});
+ for (var i = 0; i < langIds.length; i += 1) {
+ if (node.lang[langIds[i]] && node.lang[langIds[i]].custom_link) {
+ return node.lang[langIds[i]].custom_link;
+ }
+ }
+
+ return '';
+ }
+
+ function uploadImage(file, preset, callback) {
+ if (!file) {
+ return;
+ }
+
+ var formData = new FormData();
+ formData.append('ajax', '1');
+ formData.append('action', 'uploadImage');
+ formData.append('token', token);
+ formData.append('preset', preset || 'default');
+ formData.append('image', file);
+
+ fetch(ajaxUrl, {
+ method: 'POST',
+ body: formData
+ })
+ .then(function (response) {
+ return response.json();
+ })
+ .then(function (payload) {
+ if (payload && !payload.error) {
+ callback(payload);
+ }
+ });
+ }
+
+ function escapeHtml(value) {
+ return String(value)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
+ function escapeAttribute(value) {
+ return escapeHtml(value).replace(/\n/g, '
');
+ }
+
+ root.querySelector('.js-add-root-node').addEventListener('click', function () {
+ var node = createEmptyNode();
+ openNodeModal(node, 0, {
+ onSave: function (savedNode) {
+ tree.push(savedNode);
+ activeRootId = savedNode.id;
}
});
- }
-
- function escapeHtml(value) {
- return value
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''');
- }
-
- var addRootButton = root.querySelector('.js-add-root-node');
- if (addRootButton) {
- addRootButton.addEventListener('click', function () {
- tree.push(createEmptyNode());
- render();
});
- }
- render();
+ nodeModal.querySelector('.js-save-node').addEventListener('click', saveNodeModal);
+ layoutModal.querySelector('.js-save-layout').addEventListener('click', saveLayoutModal);
+ confirmModal.querySelector('.js-confirm-action').addEventListener('click', function () {
+ if (typeof confirmAction === 'function') {
+ confirmAction();
+ }
+ hideModal(confirmModal);
+ confirmAction = null;
+ });
+
+ if (window.jQuery) {
+ window.jQuery(nodeModal).on('hidden.bs.modal', function () {
+ nodeModalState = null;
+ });
+ window.jQuery(layoutModal).on('hidden.bs.modal', function () {
+ layoutModalState = null;
+ });
+ window.jQuery(confirmModal).on('hidden.bs.modal', function () {
+ confirmAction = null;
+ });
+ }
+
+ render();
}
if (document.readyState === 'loading') {
diff --git a/views/js/megamenu.js b/views/js/megamenu.js
index ecc99f4..68d0412 100644
--- a/views/js/megamenu.js
+++ b/views/js/megamenu.js
@@ -163,7 +163,7 @@
window.clearTimeout(hoverTimers.get(item));
var timer = window.setTimeout(function () {
- closeUnlockedPanels(item);
+ closeAllPanels(item);
positionBridgeForItem(item);
activateFirstSubmenuPane(item);
item.classList.add('is-open');
diff --git a/views/templates/admin/configure.tpl b/views/templates/admin/configure.tpl
index dc0276f..f83af70 100644
--- a/views/templates/admin/configure.tpl
+++ b/views/templates/admin/configure.tpl
@@ -7,6 +7,55 @@
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/views/templates/hook/megamenu.tpl b/views/templates/hook/megamenu.tpl
index 978ec3c..220310d 100644
--- a/views/templates/hook/megamenu.tpl
+++ b/views/templates/hook/megamenu.tpl
@@ -32,7 +32,7 @@
{if $nodes|count}