feat: enhance mega menu navigation with dynamic image updates and support for nested level-4 content items
This commit is contained in:
+27
@@ -215,6 +215,9 @@
|
||||
<td><span class="badge badge-info">${tabCount} tabs</span></td>
|
||||
<td><code>[plugin:tabbed-livestream id="${escapeHtml(group.id || '')}"]</code></td>
|
||||
<td class="text-center">
|
||||
<button type="button" class="btn btn-sm btn-info mr-1" onclick="duplicateGroup(${idx})" title="Nhân bản">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary mr-1" onclick="openEditGroupModal(${idx})" title="Chỉnh sửa">
|
||||
<i class="fas fa-edit"></i> Sửa
|
||||
</button>
|
||||
@@ -269,6 +272,7 @@
|
||||
<td><strong>${escapeHtml(tab.title || '')}</strong></td>
|
||||
<td class="small text-truncate" style="max-width: 180px;">${escapeHtml(tab.excerpt || '')}</td>
|
||||
<td class="text-center">
|
||||
<button type="button" class="btn btn-sm btn-info mr-1" onclick="duplicateWorkingTab(${tIdx})" title="Nhân bản"><i class="fas fa-copy"></i></button>
|
||||
<button type="button" class="btn btn-sm btn-secondary mr-1" onclick="openEditTabModal(${tIdx})"><i class="fas fa-pencil-alt"></i> Sửa</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteWorkingTab(${tIdx})"><i class="fas fa-times"></i></button>
|
||||
</td>
|
||||
@@ -347,6 +351,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Duplicate tab in modal
|
||||
function duplicateWorkingTab(tIdx) {
|
||||
if (confirm('Bạn có chắc muốn nhân bản tab này?')) {
|
||||
let originalTab = currentWorkingTabs[tIdx];
|
||||
let duplicatedTab = JSON.parse(JSON.stringify(originalTab));
|
||||
duplicatedTab.title = duplicatedTab.title + " (Copy)";
|
||||
currentWorkingTabs.push(duplicatedTab);
|
||||
renderModalTabsTable();
|
||||
}
|
||||
}
|
||||
|
||||
// Media picker inside tab modal
|
||||
function openMediaPickerForModal() {
|
||||
if (typeof SISMediaPicker !== 'undefined') {
|
||||
@@ -398,6 +413,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Duplicate group
|
||||
function duplicateGroup(idx) {
|
||||
if (confirm(`Bạn có chắc muốn nhân bản danh sách "${groups[idx].name}"?`)) {
|
||||
let originalGroup = groups[idx];
|
||||
let duplicatedGroup = JSON.parse(JSON.stringify(originalGroup));
|
||||
duplicatedGroup.name = duplicatedGroup.name + " (Copy)";
|
||||
duplicatedGroup.id = duplicatedGroup.id + "-copy-" + Math.random().toString(36).substring(2, 6);
|
||||
groups.push(duplicatedGroup);
|
||||
renderGroupsTable();
|
||||
}
|
||||
}
|
||||
|
||||
// Save form submission
|
||||
function saveAllGroups() {
|
||||
document.getElementById('groupDataInput').value = JSON.stringify(groups);
|
||||
|
||||
@@ -94,9 +94,22 @@
|
||||
const firstL2 = menu.querySelector('.l2-hover-target');
|
||||
if (firstL2) {
|
||||
firstL2.classList.add('active');
|
||||
const targetId = firstL2.getAttribute('data-target');
|
||||
const targetId = firstL2.getAttribute('data-target') || firstL2.getAttribute('data-target-fallback');
|
||||
const targetContent = document.getElementById(targetId);
|
||||
if (targetContent) targetContent.style.display = 'block';
|
||||
|
||||
const imgElem = menu.querySelector('.mega-col-img-elem');
|
||||
if (imgElem) {
|
||||
const newImg = firstL2.getAttribute('data-image');
|
||||
const defaultImg = imgElem.getAttribute('data-default-image');
|
||||
const targetImg = newImg && newImg.trim() !== '' && newImg !== 'null' ? newImg : defaultImg;
|
||||
imgElem.src = targetImg;
|
||||
if (targetImg && targetImg.includes('logo.png')) {
|
||||
imgElem.style = "object-fit: contain; background: white; padding: 20px; width: 100%; max-height: 245px;";
|
||||
} else {
|
||||
imgElem.style = "object-fit: cover; border-radius: 8px; width: 100%; max-height: 245px;";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -111,13 +124,57 @@
|
||||
// Hide all L3 content in this menu
|
||||
wrapper.querySelectorAll('.l3-content').forEach(el => el.style.display = 'none');
|
||||
|
||||
// Hide all L4 content and reset image visibility
|
||||
wrapper.querySelectorAll('.l4-content').forEach(el => el.style.display = 'none');
|
||||
const imgWrapper = wrapper.querySelector('.mega-col-image-wrapper');
|
||||
if (imgWrapper) imgWrapper.style.display = 'flex'; // image column has display: flex originally
|
||||
wrapper.querySelectorAll('.l3-hover-target').forEach(el => el.classList.remove('active', 'font-weight-bold'));
|
||||
|
||||
// Activate current
|
||||
this.classList.add('active');
|
||||
const contentId = this.getAttribute('data-target');
|
||||
const contentId = this.getAttribute('data-target') || this.getAttribute('data-target-fallback');
|
||||
const content = document.getElementById(contentId);
|
||||
if (content) {
|
||||
content.style.display = 'block';
|
||||
}
|
||||
|
||||
// Handle image override
|
||||
const imgElem = wrapper.querySelector('.mega-col-img-elem');
|
||||
if (imgElem) {
|
||||
const newImg = this.getAttribute('data-image');
|
||||
const defaultImg = imgElem.getAttribute('data-default-image');
|
||||
const targetImg = newImg && newImg.trim() !== '' && newImg !== 'null' ? newImg : defaultImg;
|
||||
imgElem.src = targetImg;
|
||||
if (targetImg && targetImg.includes('logo.png')) {
|
||||
imgElem.style = "object-fit: contain; background: white; padding: 20px; width: 100%; max-height: 245px;";
|
||||
} else {
|
||||
imgElem.style = "object-fit: cover; border-radius: 8px; width: 100%; max-height: 245px;";
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.l3-hover-target').forEach(target => {
|
||||
target.addEventListener('mouseenter', function() {
|
||||
const wrapper = this.closest('.mega-menu-content');
|
||||
if (!wrapper) return;
|
||||
|
||||
wrapper.querySelectorAll('.l3-hover-target').forEach(el => el.classList.remove('active', 'font-weight-bold'));
|
||||
this.classList.add('active', 'font-weight-bold');
|
||||
|
||||
wrapper.querySelectorAll('.l4-content').forEach(el => el.style.display = 'none');
|
||||
|
||||
const hasChildren = this.getAttribute('data-has-children') === 'true';
|
||||
const imgWrapper = wrapper.querySelector('.mega-col-image-wrapper');
|
||||
|
||||
if (hasChildren) {
|
||||
const contentId = this.getAttribute('data-target');
|
||||
const content = document.getElementById(contentId);
|
||||
if (content) content.style.display = 'block';
|
||||
if (imgWrapper) imgWrapper.style.display = 'none';
|
||||
} else {
|
||||
if (imgWrapper) imgWrapper.style.display = 'flex';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -314,6 +371,32 @@
|
||||
color: var(--color-white) !important;
|
||||
}
|
||||
|
||||
/* Column 4: Level 4 */
|
||||
.submenu-l4 {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.submenu-l4 .menu-item-l4 {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.submenu-l4 .menu-item-l4 a {
|
||||
display: block;
|
||||
padding: 12px 15px;
|
||||
color: #111827 !important;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 4px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.submenu-l4 .menu-item-l4 a:hover {
|
||||
background-color: var(--color-brand) !important;
|
||||
color: var(--color-white) !important;
|
||||
}
|
||||
|
||||
/* Hide the mobile back button and title on desktop */
|
||||
.mc--main-menu .megamenu.menu-item .submenu-wrapper .button-back,
|
||||
.mc--main-menu .megamenu.menu-item .submenu-wrapper .navigation-title {
|
||||
@@ -389,7 +472,7 @@
|
||||
<!-- Column 2: Level 2 Links -->
|
||||
<div class="mega-col-level2" th:if="${not #lists.isEmpty(item.children)}">
|
||||
<ul class="submenu-l2">
|
||||
<li class="menu-item-l2 l2-hover-target" th:each="child : ${item.children}" th:attr="data-target='l3-' + ${item.id} + '-' + ${child.id}">
|
||||
<li class="menu-item-l2 l2-hover-target" th:each="child : ${item.children}" th:data-target-fallback="'l3-' + ${item.id} + '-' + ${child.id}" th:attr="data-target='l3-' + ${item.id} + '-' + ${child.id}" th:data-image="${child.imageUrl}">
|
||||
<a th:href="${child.url}" th:text="${child.label}">Child Item</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -401,23 +484,37 @@
|
||||
<th:block th:each="child : ${item.children}">
|
||||
<ul th:id="'l3-' + ${item.id} + '-' + ${child.id}" class="submenu-l3 l3-content" style="display: none;">
|
||||
<th:block th:if="${not #lists.isEmpty(child.children)}">
|
||||
<li class="menu-item-l3" th:each="grandchild : ${child.children}">
|
||||
<a th:href="${grandchild.url}" th:text="${grandchild.label}" th:classappend="${not #lists.isEmpty(grandchild.children) ? 'font-weight-bold' : ''}">Grandchild Item</a>
|
||||
<ul th:if="${not #lists.isEmpty(grandchild.children)}" class="submenu-l4" style="padding-left: 15px; margin-top: 5px; list-style-type: none;">
|
||||
<li class="menu-item-l4" th:each="greatgrandchild : ${grandchild.children}" style="margin-bottom: 5px;">
|
||||
<a th:href="${greatgrandchild.url}" th:text="${greatgrandchild.label}" style="font-size: 13px; font-weight: normal; color: #555; text-transform: none;">Great-Grandchild Item</a>
|
||||
</li>
|
||||
</ul>
|
||||
<li class="menu-item-l3 l3-hover-target" th:each="grandchild : ${child.children}" th:attr="data-target='l4-' + ${item.id} + '-' + ${child.id} + '-' + ${grandchild.id}" th:data-has-children="${not #lists.isEmpty(grandchild.children)}">
|
||||
<a th:href="${grandchild.url}" th:text="${grandchild.label}">Grandchild Item</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</th:block>
|
||||
</div>
|
||||
|
||||
<!-- Column 4: Featured Image / Media Banner -->
|
||||
<div class="mega-col-image">
|
||||
<img th:if="${not #strings.isEmpty(item.imageUrl)}" th:src="${item.imageUrl}" style="object-fit: cover; border-radius: 8px; width: 100%; max-height: 245px;"/>
|
||||
<img th:if="${#strings.isEmpty(item.imageUrl)}" th:src="@{/theme-assets/umass/images/logo.png}" style="object-fit: contain; background: white; padding: 20px; width: 100%; max-height: 245px;"/>
|
||||
<!-- Column 4: Featured Image or Level 4 Links -->
|
||||
<div class="mega-col-level4-and-image" style="flex: 1; border-left: 1px solid #d1d5db; padding-left: 20px;">
|
||||
<!-- Level 4 Containers -->
|
||||
<div class="level4-wrapper">
|
||||
<th:block th:each="child : ${item.children}">
|
||||
<th:block th:each="grandchild : ${child.children}">
|
||||
<ul th:id="'l4-' + ${item.id} + '-' + ${child.id} + '-' + ${grandchild.id}" class="submenu-l4 l4-content" style="display: none; padding:0; margin:0; list-style:none;">
|
||||
<li class="menu-item-l4" th:each="greatgrandchild : ${grandchild.children}">
|
||||
<a th:href="${greatgrandchild.url}" th:text="${greatgrandchild.label}">Great-Grandchild Item</a>
|
||||
</li>
|
||||
</ul>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
|
||||
<!-- Featured Image -->
|
||||
<div class="mega-col-image-wrapper mega-col-image" style="border-left: none; padding-left: 0;">
|
||||
<img class="mega-col-img-elem"
|
||||
th:data-default-image="${not #strings.isEmpty(item.imageUrl) ? item.imageUrl : '/theme-assets/umass/images/logo.png'}"
|
||||
th:src="${not #strings.isEmpty(item.imageUrl) ? item.imageUrl : '/theme-assets/umass/images/logo.png'}"
|
||||
th:style="${not #strings.isEmpty(item.imageUrl) ? 'object-fit: cover; border-radius: 8px; width: 100%; max-height: 245px;' : 'object-fit: contain; background: white; padding: 20px; width: 100%; max-height: 245px;'}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Mega Menu Layout -->
|
||||
|
||||
Reference in New Issue
Block a user