feat: implement Bento layout support and Editor.js integration for dynamic post lists
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
<style>
|
||||
/* Bento Layout 01 Design System */
|
||||
.bento-gallery {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(12, 1fr) !important;
|
||||
grid-template-rows: repeat(2, 220px);
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.bento-item {
|
||||
position: relative;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.bento-item img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.bento-item:hover img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.bento-item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.85) 0%, rgba(0, 0, 0, 0.25) 55%, transparent 100%);
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.bento-content {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 16px;
|
||||
z-index: 2;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.bento-content h2,
|
||||
.bento-content h3 {
|
||||
margin: 0;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.bento-content h3 {
|
||||
font-size: 0.925rem;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bento-content h2 {
|
||||
font-size: 1.15rem;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Bento Column Assignments */
|
||||
.left-top,
|
||||
.left-bottom {
|
||||
grid-column: span 3 !important;
|
||||
}
|
||||
|
||||
.center-featured {
|
||||
grid-column: span 6 !important;
|
||||
grid-row: span 2 !important;
|
||||
}
|
||||
|
||||
.right-top,
|
||||
.right-bottom {
|
||||
grid-column: span 3 !important;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media (max-width: 992px) {
|
||||
.bento-gallery {
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
.center-featured {
|
||||
grid-column: span 12 !important;
|
||||
grid-row: span 1 !important;
|
||||
height: 280px;
|
||||
}
|
||||
.left-top,
|
||||
.left-bottom,
|
||||
.right-top,
|
||||
.right-bottom {
|
||||
grid-column: span 6 !important;
|
||||
height: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.left-top,
|
||||
.left-bottom,
|
||||
.right-top,
|
||||
.right-bottom {
|
||||
grid-column: span 12 !important;
|
||||
height: 160px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="bento-gallery" th:if="${posts != null}">
|
||||
<th:block
|
||||
th:with="filteredPosts=${posts.?[ (block.data['layoutFilter'] == null or block.data['layoutFilter'] == 'ALL' or (layout != null and #strings.equals(layout.name(), block.data['layoutFilter']))) and (block.data['categoryFilter'] == null or #strings.isEmpty(block.data['categoryFilter']) or (category != null and #strings.containsIgnoreCase(category.name, block.data['categoryFilter']))) and (block.data['tagFilter'] == null or #strings.isEmpty(block.data['tagFilter']) or (tags != null and #lists.contains(tags.![name], block.data['tagFilter']))) ]}"
|
||||
>
|
||||
<!-- 1. Ô trên bên trái -->
|
||||
<div class="bento-item left-top" th:if="${#lists.size(filteredPosts) > 0}" th:with="p=${filteredPosts[0]}">
|
||||
<a th:href="@{'/posts/' + ${p.slug}}" class="text-decoration-none text-white d-block h-100 w-100">
|
||||
<img th:src="${p.featuredImage}" th:alt="${p.title}" />
|
||||
<div class="bento-content">
|
||||
<h3 th:utext="${p.title}">Post Title 1</h3>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 2. Ô dưới bên trái -->
|
||||
<div class="bento-item left-bottom" th:if="${#lists.size(filteredPosts) > 1}" th:with="p=${filteredPosts[1]}">
|
||||
<a th:href="@{'/posts/' + ${p.slug}}" class="text-decoration-none text-white d-block h-100 w-100">
|
||||
<img th:src="${p.featuredImage}" th:alt="${p.title}" />
|
||||
<div class="bento-content">
|
||||
<h3 th:utext="${p.title}">Post Title 2</h3>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 3. Ô chính giữa (Hình lớn nổi bật) -->
|
||||
<div class="bento-item center-featured" th:if="${#lists.size(filteredPosts) > 2}" th:with="p=${filteredPosts[2]}">
|
||||
<a th:href="@{'/posts/' + ${p.slug}}" class="text-decoration-none text-white d-block h-100 w-100">
|
||||
<img th:src="${p.featuredImage}" th:alt="${p.title}" />
|
||||
<div class="bento-content">
|
||||
<h2 th:utext="${p.title}">Featured Post Title</h2>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 4. Ô trên bên phải -->
|
||||
<div class="bento-item right-top" th:if="${#lists.size(filteredPosts) > 3}" th:with="p=${filteredPosts[3]}">
|
||||
<a th:href="@{'/posts/' + ${p.slug}}" class="text-decoration-none text-white d-block h-100 w-100">
|
||||
<img th:src="${p.featuredImage}" th:alt="${p.title}" />
|
||||
<div class="bento-content">
|
||||
<h3 th:utext="${p.title}">Post Title 4</h3>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 5. Ô dưới bên phải -->
|
||||
<div class="bento-item right-bottom" th:if="${#lists.size(filteredPosts) > 4}" th:with="p=${filteredPosts[4]}">
|
||||
<a th:href="@{'/posts/' + ${p.slug}}" class="text-decoration-none text-white d-block h-100 w-100">
|
||||
<img th:src="${p.featuredImage}" th:alt="${p.title}" />
|
||||
<div class="bento-content">
|
||||
<h3 th:utext="${p.title}">Post Title 5</h3>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</th:block>
|
||||
</div>
|
||||
@@ -0,0 +1,343 @@
|
||||
<style>
|
||||
/* ===================================================
|
||||
CARD GRID LAYOUT DESIGN SYSTEM & STYLES
|
||||
=================================================== */
|
||||
.news-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* FILTER TABS */
|
||||
.filter-tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 8px 18px;
|
||||
border: none;
|
||||
background-color: #e2e8f0;
|
||||
color: #475569;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
background-color: #cbd5e1;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background-color: #0284c7;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* CARD GRID */
|
||||
.news-grid {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(3, 1fr) !important;
|
||||
gap: 24px;
|
||||
margin-bottom: 40px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.news-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
||||
cursor: pointer;
|
||||
border: 1px solid #f1f5f9;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.news-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-thumb {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
overflow: hidden;
|
||||
background-color: #e2e8f0;
|
||||
}
|
||||
|
||||
.card-thumb img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.news-card:hover .card-thumb img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.year-badge {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background-color: #0284c7;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 16px 18px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #0284c7;
|
||||
line-height: 1.45;
|
||||
margin: 0;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* PAGINATION */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
list-style: none;
|
||||
gap: 8px;
|
||||
margin-top: 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.page-link {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
padding: 0 10px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #e2e8f0;
|
||||
background-color: #ffffff;
|
||||
color: #64748b;
|
||||
text-decoration: none !important;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.page-link:hover {
|
||||
border-color: #0284c7;
|
||||
color: #0284c7;
|
||||
}
|
||||
|
||||
.page-link.active {
|
||||
background-color: #0284c7;
|
||||
color: #ffffff;
|
||||
border-color: #0284c7;
|
||||
}
|
||||
|
||||
.page-dots {
|
||||
padding: 0 4px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
/* RESPONSIVE */
|
||||
@media (max-width: 992px) {
|
||||
.news-grid {
|
||||
grid-template-columns: repeat(2, 1fr) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.news-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
.filter-tabs {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="news-container" th:if="${posts != null}">
|
||||
<!-- 1. FILTER TABS (Every selected Category/Tag in posts.js becomes a Tab) -->
|
||||
<div class="filter-tabs"
|
||||
th:if="${block.data['enableTabs'] == null or block.data['enableTabs'] == true}"
|
||||
th:data-configured-tabs="${(block.data['categoryFilter'] != null ? block.data['categoryFilter'] : '') + (block.data['tagFilter'] != null and !#strings.isEmpty(block.data['tagFilter']) ? (block.data['categoryFilter'] != null and !#strings.isEmpty(block.data['categoryFilter']) ? ',' : '') + block.data['tagFilter'] : '')}">
|
||||
<button type="button" class="tab-btn active" data-filter="ALL">Tất cả</button>
|
||||
</div>
|
||||
|
||||
<!-- 2. CARD GRID -->
|
||||
<div class="news-grid" th:data-items-per-page="${block.data['itemsPerPage'] != null ? block.data['itemsPerPage'] : (block.data['limit'] != null ? block.data['limit'] : 9)}">
|
||||
<th:block th:each="p : ${posts}" th:if="${(block.data['layoutFilter'] == null or block.data['layoutFilter'] == 'ALL' or (p.layout != null and #strings.equals(p.layout.name(), block.data['layoutFilter'])))}">
|
||||
<a th:href="@{'/posts/' + ${p.slug}}"
|
||||
class="news-card"
|
||||
th:data-category="${p.category != null ? p.category.name : ''}"
|
||||
th:data-tags="${p.tags != null ? #strings.arrayJoin(p.tags.![name], ',') : ''}">
|
||||
<div class="card-thumb">
|
||||
<img th:src="${p.featuredImage}" th:alt="${p.title}" />
|
||||
<span class="year-badge" th:if="${p.createdDate != null}" th:text="${#temporals.format(p.createdDate, 'yyyy')}">2026</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title" th:utext="${p.title}">Post Title</h3>
|
||||
</div>
|
||||
</a>
|
||||
</th:block>
|
||||
</div>
|
||||
|
||||
<!-- 3. PAGINATION -->
|
||||
<ul class="pagination" th:if="${block.data['enablePagination'] == null or block.data['enablePagination'] == true}"></ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
function initCardGridContainers() {
|
||||
const containers = document.querySelectorAll('.news-container');
|
||||
containers.forEach(function (container) {
|
||||
if (container.dataset.gridInitialized) return;
|
||||
container.dataset.gridInitialized = 'true';
|
||||
|
||||
const tabsContainer = container.querySelector('.filter-tabs');
|
||||
const grid = container.querySelector('.news-grid');
|
||||
const pagination = container.querySelector('.pagination');
|
||||
if (!grid) return;
|
||||
|
||||
const cards = Array.from(grid.querySelectorAll('.news-card'));
|
||||
const itemsPerPage = parseInt(grid.getAttribute('data-items-per-page')) || 9;
|
||||
let currentPage = 1;
|
||||
let activeFilter = 'ALL';
|
||||
|
||||
// Render Tabs from posts.js multi-choice selection or extracted cards
|
||||
if (tabsContainer) {
|
||||
const rawConfigured = tabsContainer.getAttribute('data-configured-tabs') || '';
|
||||
let tabNames = rawConfigured.split(',').map(function (s) { return s.trim(); }).filter(Boolean);
|
||||
|
||||
// Fallback to extracting from cards if no specific multi-choice selection was made
|
||||
if (tabNames.length === 0) {
|
||||
const foundSet = new Set();
|
||||
cards.forEach(function (card) {
|
||||
const cat = card.getAttribute('data-category');
|
||||
if (cat) foundSet.add(cat.trim());
|
||||
const tags = (card.getAttribute('data-tags') || '').split(',');
|
||||
tags.forEach(function (t) { if (t.trim()) foundSet.add(t.trim()); });
|
||||
});
|
||||
tabNames = Array.from(foundSet);
|
||||
}
|
||||
|
||||
// Build tab button elements
|
||||
tabNames.forEach(function (name) {
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'tab-btn';
|
||||
btn.setAttribute('data-filter', name);
|
||||
btn.textContent = name;
|
||||
tabsContainer.appendChild(btn);
|
||||
});
|
||||
|
||||
// Single active tab switching
|
||||
tabsContainer.addEventListener('click', function (e) {
|
||||
const btn = e.target.closest('.tab-btn');
|
||||
if (!btn) return;
|
||||
activeFilter = btn.getAttribute('data-filter');
|
||||
|
||||
tabsContainer.querySelectorAll('.tab-btn').forEach(function (b) {
|
||||
if (b.getAttribute('data-filter') === activeFilter) {
|
||||
b.classList.add('active');
|
||||
} else {
|
||||
b.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
currentPage = 1;
|
||||
render();
|
||||
});
|
||||
}
|
||||
|
||||
// Render card visibility based on active tab & calculate pagination
|
||||
function render() {
|
||||
let visibleCards = cards.filter(function (card) {
|
||||
if (activeFilter === 'ALL') return true;
|
||||
const cardCat = (card.getAttribute('data-category') || '').trim();
|
||||
const cardTags = (card.getAttribute('data-tags') || '').split(',').map(function (s) { return s.trim(); });
|
||||
return cardCat === activeFilter || cardTags.includes(activeFilter);
|
||||
});
|
||||
|
||||
const totalPages = Math.ceil(visibleCards.length / itemsPerPage) || 1;
|
||||
if (currentPage > totalPages) currentPage = totalPages;
|
||||
|
||||
// Hide all
|
||||
cards.forEach(function (c) { c.style.display = 'none'; });
|
||||
|
||||
// Show current page slice
|
||||
const startIdx = (currentPage - 1) * itemsPerPage;
|
||||
const endIdx = startIdx + itemsPerPage;
|
||||
visibleCards.slice(startIdx, endIdx).forEach(function (c) {
|
||||
c.style.display = 'flex';
|
||||
});
|
||||
|
||||
// Render Pagination
|
||||
if (pagination) {
|
||||
pagination.innerHTML = '';
|
||||
if (totalPages <= 1) return;
|
||||
|
||||
const prevLi = document.createElement('li');
|
||||
prevLi.innerHTML = '<span class="page-link">«</span>';
|
||||
prevLi.addEventListener('click', function () {
|
||||
if (currentPage > 1) { currentPage--; render(); }
|
||||
});
|
||||
pagination.appendChild(prevLi);
|
||||
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('span');
|
||||
a.className = 'page-link' + (i === currentPage ? ' active' : '');
|
||||
a.textContent = i;
|
||||
a.addEventListener('click', function () {
|
||||
currentPage = i;
|
||||
render();
|
||||
});
|
||||
li.appendChild(a);
|
||||
pagination.appendChild(li);
|
||||
}
|
||||
|
||||
const nextLi = document.createElement('li');
|
||||
nextLi.innerHTML = '<span class="page-link">»</span>';
|
||||
nextLi.addEventListener('click', function () {
|
||||
if (currentPage < totalPages) { currentPage++; render(); }
|
||||
});
|
||||
pagination.appendChild(nextLi);
|
||||
}
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
||||
initCardGridContainers();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', initCardGridContainers);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
@@ -1,155 +0,0 @@
|
||||
{
|
||||
"time": 1785560196216,
|
||||
"blocks": [
|
||||
{
|
||||
"id": "HEo3OKYI07",
|
||||
"type": "stickyNav",
|
||||
"data": {
|
||||
"title": "Tầm nhìn - Sứ mệnh",
|
||||
"bgColor": "#ffffff",
|
||||
"items": [
|
||||
{
|
||||
"label": "Giới thiệu tổng quan",
|
||||
"link": "/page/gioi-thieu-tong-quan"
|
||||
},
|
||||
{
|
||||
"label": "Lịch sử hình thành",
|
||||
"link": "/page/lich-su-hinh-thanh"
|
||||
},
|
||||
{
|
||||
"label": "Tầm nhìn - Sứ mệnh - Giá trị cốt lõi",
|
||||
"link": "/page/tam-nhin-su-menh-gia-tri-cot-loi"
|
||||
},
|
||||
{
|
||||
"label": "Ban lãnh đạo",
|
||||
"link": "/page/doi-ngu-lanh-dao"
|
||||
},
|
||||
{
|
||||
"label": "Các đơn vị",
|
||||
"link": "/page/cac-don-vi"
|
||||
},
|
||||
{
|
||||
"label": "Thành tựu và giải thưởng",
|
||||
"link": "/page/thanh-tuu-va-giai-thuong"
|
||||
}
|
||||
],
|
||||
"sticky": true,
|
||||
"stretched": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "_9-7doWIJA",
|
||||
"type": "grid",
|
||||
"data": {
|
||||
"cols": 2,
|
||||
"globalClass": "",
|
||||
"globalId": "",
|
||||
"type1": "html",
|
||||
"col1": "\n<div class=\"relative pt-4\">\n <div class=\"absolute md:-left-4 -left-3 md:-top-5 -top-2 md:size-12 size-8 bg-primary-100 rounded-full\"></div>\n <h2 class=\"display-3 text-primary-500 uppercase mb-4 block z-[1] relative\">TẦM NHÌN</h2>\n <div class=\"flex flex-col space-y-4\">\n <div class=\"bg-white p-4 md:p-6 rounded-lg overview-wrapper-shadow\">\n <p class=\"body-1 text-gray-800 text-justify !font-medium\">Trở thành bệnh viện đại học dẫn đầu Việt Nam đạt chuẩn quốc tế</p>\n </div>\n </div>\n</div>\n",
|
||||
"imgUrl1": "",
|
||||
"ytUrl1": "",
|
||||
"accTitle1": "",
|
||||
"accContent1": "",
|
||||
"class1": "relative z-10",
|
||||
"id1": "",
|
||||
"width1": 6,
|
||||
"type2": "image",
|
||||
"col2": {
|
||||
"file": {
|
||||
"extension": "webp",
|
||||
"size": 98106,
|
||||
"name": "dotquy203.webp",
|
||||
"url": "/uploads/2026/08/dotquy203.webp"
|
||||
},
|
||||
"caption": "",
|
||||
"withBorder": false,
|
||||
"stretched": false,
|
||||
"withBackground": false
|
||||
},
|
||||
"imgUrl2": "",
|
||||
"ytUrl2": "",
|
||||
"accTitle2": "",
|
||||
"accContent2": "",
|
||||
"class2": "shadow-lg rounded",
|
||||
"id2": "",
|
||||
"width2": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "SL3DLP54-m",
|
||||
"type": "grid",
|
||||
"data": {
|
||||
"cols": 2,
|
||||
"globalClass": "",
|
||||
"globalId": "",
|
||||
"type1": "image",
|
||||
"col1": {
|
||||
"file": {
|
||||
"url": "/uploads/2026/07/dotquy201.webp"
|
||||
},
|
||||
"caption": "",
|
||||
"withBorder": false,
|
||||
"stretched": false,
|
||||
"withBackground": false
|
||||
},
|
||||
"imgUrl1": "",
|
||||
"ytUrl1": "",
|
||||
"accTitle1": "",
|
||||
"accContent1": "",
|
||||
"class1": "shadow-lg rounded",
|
||||
"id1": "",
|
||||
"width1": 6,
|
||||
"type2": "html",
|
||||
"col2": "\n<div class=\"relative pt-4\">\n <div class=\"absolute md:-left-5 -left-4 md:-top-5 -top-2 md:size-12 size-8 bg-primary-100 rounded-full\"></div>\n <h2 class=\"display-3 text-primary-500 mb-4 relative z-[1]\">SỨ MỆNH</h2>\n <div class=\"bg-white p-4 md:p-6 rounded-lg overview-wrapper-shadow\">\n <p class=\"body-1 text-primary-500 text-justify !font-medium\">Mang đến giải pháp chăm sóc sức khỏe tối ưu từ sự tích hợp giữa điều trị, nghiên cứu và đào tạo</p>\n </div>\n</div>\n",
|
||||
"imgUrl2": "",
|
||||
"ytUrl2": "",
|
||||
"accTitle2": "",
|
||||
"accContent2": "",
|
||||
"class2": "",
|
||||
"id2": "",
|
||||
"width2": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bJ3ycvyeZT",
|
||||
"type": "hovercard",
|
||||
"data": {
|
||||
"cols": 4,
|
||||
"styleVariant": "v2",
|
||||
"globalClass": "sis-hover-card-v2-section",
|
||||
"globalId": "",
|
||||
"items": [
|
||||
{
|
||||
"title": "Tiên phong",
|
||||
"description": "Phát triển và ứng dụng kỹ thuật tiên tiến vào điều trị, giảng dạy, nghiên cứu",
|
||||
"imageUrl": "/uploads/2026/08/core-1.webp",
|
||||
"hoverImageUrl": "/uploads/2026/08/core-hover-1.webp",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"title": "Thấu hiểu",
|
||||
"description": "Nỗi đau thể xác và tinh thần của người bệnh để đưa ra giải pháp điều trị tối ưu",
|
||||
"imageUrl": "/uploads/2026/08/core-2.webp",
|
||||
"hoverImageUrl": "/uploads/2026/08/core-hover-2.webp",
|
||||
"active": false
|
||||
},
|
||||
{
|
||||
"title": "Chuẩn mực",
|
||||
"description": "Về y đức và học thuật của người thầy giáo – thầy thuốc",
|
||||
"imageUrl": "/uploads/2026/08/core-3.webp",
|
||||
"hoverImageUrl": "/uploads/2026/08/core-hover-3.webp",
|
||||
"active": false
|
||||
},
|
||||
{
|
||||
"title": "An toàn",
|
||||
"description": "Cho người bệnh, nhân viên y tế và cộng đồng",
|
||||
"imageUrl": "/uploads/2026/08/core-4.webp",
|
||||
"hoverImageUrl": "/uploads/2026/08/core-hover-4.webp",
|
||||
"active": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": "2.31.0-rc.7"
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
.sis-hover-card-grid {
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
.sis-hover-card-grid .row {
|
||||
display: flex !important;
|
||||
align-items: stretch !important;
|
||||
}
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"] {
|
||||
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) !important;
|
||||
cursor: pointer;
|
||||
background-color: #ffffff !important;
|
||||
border: 1px solid #e6f0fa !important;
|
||||
border-radius: 12px !important;
|
||||
box-shadow: 0 4px 15px rgba(0, 37, 84, 0.08) !important;
|
||||
padding: 30px 20px !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
align-items: center !important;
|
||||
justify-content: flex-start !important;
|
||||
min-height: 260px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"] img {
|
||||
height: 80px !important;
|
||||
width: auto !important;
|
||||
max-width: 100% !important;
|
||||
object-fit: contain !important;
|
||||
margin-bottom: 16px !important;
|
||||
transition:
|
||||
filter 0.4s ease,
|
||||
transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) !important;
|
||||
}
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"] h3 {
|
||||
color: #0d71ba !important;
|
||||
font-size: 1.25rem !important;
|
||||
font-weight: 700 !important;
|
||||
margin-bottom: 0 !important;
|
||||
transition: color 0.4s ease !important;
|
||||
}
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"] p {
|
||||
color: rgba(255, 255, 255, 0) !important;
|
||||
font-size: 0.9rem !important;
|
||||
line-height: 1.5 !important;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
transition:
|
||||
max-height 0.4s ease,
|
||||
opacity 0.4s ease,
|
||||
color 0.4s ease,
|
||||
margin-top 0.4s ease !important;
|
||||
}
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"]:hover,
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"].active {
|
||||
background-color: #0d71ba !important;
|
||||
border-color: #0d71ba !important;
|
||||
transform: translateY(-8px) !important;
|
||||
box-shadow: 0 12px 25px rgba(13, 113, 186, 0.3) !important;
|
||||
}
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"]:hover h3,
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"].active h3 {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"]:hover p,
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"].active p {
|
||||
color: #ffffff !important;
|
||||
opacity: 1 !important;
|
||||
max-height: 150px;
|
||||
margin-top: 12px !important;
|
||||
}
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"]:hover img,
|
||||
.sis-hover-card-grid .row > [class*=\"col-\"].active img {
|
||||
/* filter: brightness(0) invert(1) !important; */
|
||||
transform: scale(1.05) !important;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var cards = document.querySelectorAll('.sis-hover-card-grid .row > [class*=\"col-\"]');
|
||||
if (cards.length > 0) {
|
||||
cards.forEach(function (card) {
|
||||
card.addEventListener('click', function () {
|
||||
cards.forEach(function (c) { c.classList.remove('active'); });
|
||||
card.classList.add('active');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,5 +0,0 @@
|
||||
<section class="sis-hover-card-v2-section py-5">
|
||||
<div class="container">
|
||||
<h2 class="text-center display-4 text-primary-600 xl:mb-6 md:mb-4 mb-3 max-md:px-4 uppercase font-weight-bold">GIÁ TRỊ CỐT LÕI</h2>
|
||||
</div>
|
||||
</section>
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user