added sceleton for loading
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
<div class="item-card">
|
<div class="item-card">
|
||||||
<a [routerLink]="['/item', item.itemID] | langRoute" class="item-link">
|
<a [routerLink]="['/item', item.itemID] | langRoute" class="item-link">
|
||||||
<div class="item-image">
|
<div class="item-image">
|
||||||
<img [src]="getMainImage(item)" [alt]="item.name" loading="lazy" decoding="async" width="300" height="300" />
|
<img [src]="getMainImage(item)" [alt]="item.name" loading="lazy" decoding="async" />
|
||||||
@if (item.discount > 0) {
|
@if (item.discount > 0) {
|
||||||
<div class="discount-badge">-{{ item.discount }}%</div>
|
<div class="discount-badge">-{{ item.discount }}%</div>
|
||||||
}
|
}
|
||||||
@@ -50,14 +50,24 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (loading() && items().length > 0) {
|
@if (loading() && items().length > 0) {
|
||||||
<div class="loading-more">
|
@for (i of skeletonSlots; track i) {
|
||||||
<div class="spinner"></div>
|
<div class="item-card skeleton-card">
|
||||||
<p>{{ 'category.loadingMore' | translate }}</p>
|
<div class="item-link">
|
||||||
</div>
|
<div class="item-image skeleton-image"></div>
|
||||||
}
|
<div class="item-details">
|
||||||
|
<div class="skeleton-line skeleton-title"></div>
|
||||||
|
<div class="skeleton-line skeleton-rating"></div>
|
||||||
|
<div class="skeleton-line skeleton-price"></div>
|
||||||
|
<div class="skeleton-line skeleton-stock"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="skeleton-btn"></div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (!hasMore() && items().length > 0) {
|
@if (!hasMore() && items().length > 0) {
|
||||||
<div class="no-more">
|
<div class="no-more">
|
||||||
|
|||||||
@@ -141,7 +141,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: #f5f5f5;
|
background: #f0f0f0;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
background: white;
|
background: white;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
transition: transform 0.3s ease;
|
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover img {
|
&:hover img {
|
||||||
@@ -290,11 +290,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-more {
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -315,6 +310,59 @@
|
|||||||
padding: 40px 20px;
|
padding: 40px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skeleton loading cards
|
||||||
|
.skeleton-card {
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
.skeleton-image {
|
||||||
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: shimmer 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-line {
|
||||||
|
border-radius: 6px;
|
||||||
|
background: linear-gradient(90deg, #e8e8e8 25%, #d8d8d8 50%, #e8e8e8 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: shimmer 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-title {
|
||||||
|
height: 16px;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-rating {
|
||||||
|
height: 12px;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-price {
|
||||||
|
height: 18px;
|
||||||
|
width: 40%;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-stock {
|
||||||
|
height: 6px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-btn {
|
||||||
|
height: 42px;
|
||||||
|
background: linear-gradient(90deg, #5a8a85 25%, #497671 50%, #5a8a85 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: shimmer 1.5s infinite;
|
||||||
|
border-radius: 0 0 13px 13px;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shimmer {
|
||||||
|
0% { background-position: 200% 0; }
|
||||||
|
100% { background-position: -200% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
// Responsive
|
// Responsive
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.items-grid {
|
.items-grid {
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export class CategoryComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.scrollTimeout = setTimeout(() => {
|
this.scrollTimeout = setTimeout(() => {
|
||||||
const scrollPosition = window.innerHeight + window.scrollY;
|
const scrollPosition = window.innerHeight + window.scrollY;
|
||||||
const bottomPosition = document.documentElement.scrollHeight - 500;
|
const bottomPosition = document.documentElement.scrollHeight - 1200;
|
||||||
|
|
||||||
if (scrollPosition >= bottomPosition && !this.loading() && this.hasMore() && !this.isLoadingMore) {
|
if (scrollPosition >= bottomPosition && !this.loading() && this.hasMore() && !this.isLoadingMore) {
|
||||||
this.loadItems();
|
this.loadItems();
|
||||||
@@ -104,6 +104,7 @@ export class CategoryComponent implements OnInit, OnDestroy {
|
|||||||
this.cartService.addItem(itemID);
|
this.cartService.addItem(itemID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly skeletonSlots = Array.from({ length: 8 });
|
||||||
readonly getDiscountedPrice = getDiscountedPrice;
|
readonly getDiscountedPrice = getDiscountedPrice;
|
||||||
readonly getMainImage = getMainImage;
|
readonly getMainImage = getMainImage;
|
||||||
readonly trackByItemId = trackByItemId;
|
readonly trackByItemId = trackByItemId;
|
||||||
|
|||||||
Reference in New Issue
Block a user