mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Add store items pagination and fix modal backdrop blur across dashboards.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
4d480799b0
commit
e6670ed06f
@@ -1,9 +1,8 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(15, 23, 42, 0.25);
|
isolation: isolate;
|
||||||
backdrop-filter: blur(6px);
|
background: transparent;
|
||||||
-webkit-backdrop-filter: blur(6px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -11,6 +10,19 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tint + blur on ::before so opacity animations on .overlay cannot disable backdrop-filter. */
|
||||||
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: var(--modal-overlay-bg);
|
||||||
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 520px;
|
max-width: 520px;
|
||||||
@@ -135,14 +147,6 @@
|
|||||||
box-shadow: 0 6px 16px rgba(var(--primary-rgb) / 0.35);
|
box-shadow: 0 6px 16px rgba(var(--primary-rgb) / 0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayIn {
|
|
||||||
animation: overlayFadeIn 0.22s ease forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlayOut {
|
|
||||||
animation: overlayFadeOut 0.22s ease forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modalIn {
|
.modalIn {
|
||||||
animation: modalFadeIn 0.25s ease forwards;
|
animation: modalFadeIn 0.25s ease forwards;
|
||||||
}
|
}
|
||||||
@@ -150,17 +154,6 @@
|
|||||||
.modalOut {
|
.modalOut {
|
||||||
animation: modalFadeOut 0.22s ease forwards;
|
animation: modalFadeOut 0.22s ease forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes overlayFadeIn {
|
|
||||||
from { opacity: 0; }
|
|
||||||
to { opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes overlayFadeOut {
|
|
||||||
from { opacity: 1; }
|
|
||||||
to { opacity: 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes modalFadeIn {
|
@keyframes modalFadeIn {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(15, 23, 42, 0.25);
|
isolation: isolate;
|
||||||
backdrop-filter: blur(6px);
|
background: transparent;
|
||||||
-webkit-backdrop-filter: blur(6px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -11,6 +10,19 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tint + blur on ::before so opacity animations on .overlay cannot disable backdrop-filter. */
|
||||||
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: var(--modal-overlay-bg);
|
||||||
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
@@ -147,14 +159,6 @@
|
|||||||
box-shadow: 0 6px 16px rgba(var(--primary-rgb) / 0.35);
|
box-shadow: 0 6px 16px rgba(var(--primary-rgb) / 0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayIn {
|
|
||||||
animation: overlayFadeIn 0.22s ease forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlayOut {
|
|
||||||
animation: overlayFadeOut 0.22s ease forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modalIn {
|
.modalIn {
|
||||||
animation: modalFadeIn 0.25s ease forwards;
|
animation: modalFadeIn 0.25s ease forwards;
|
||||||
}
|
}
|
||||||
@@ -162,17 +166,6 @@
|
|||||||
.modalOut {
|
.modalOut {
|
||||||
animation: modalFadeOut 0.22s ease forwards;
|
animation: modalFadeOut 0.22s ease forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes overlayFadeIn {
|
|
||||||
from { opacity: 0; }
|
|
||||||
to { opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes overlayFadeOut {
|
|
||||||
from { opacity: 1; }
|
|
||||||
to { opacity: 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes modalFadeIn {
|
@keyframes modalFadeIn {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(15, 23, 42, 0.28);
|
isolation: isolate;
|
||||||
backdrop-filter: blur(6px);
|
background: transparent;
|
||||||
-webkit-backdrop-filter: blur(6px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -11,6 +10,19 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tint + blur on ::before so opacity animations on .overlay cannot disable backdrop-filter. */
|
||||||
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: var(--modal-overlay-bg);
|
||||||
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -174,13 +186,8 @@
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayIn { animation: overlayFadeIn 0.22s ease forwards; }
|
|
||||||
.overlayOut { animation: overlayFadeOut 0.22s ease forwards; }
|
|
||||||
.modalIn { animation: modalFadeIn 0.25s ease forwards; }
|
.modalIn { animation: modalFadeIn 0.25s ease forwards; }
|
||||||
.modalOut { animation: modalFadeOut 0.22s ease forwards; }
|
.modalOut { animation: modalFadeOut 0.22s ease forwards; }
|
||||||
|
|
||||||
@keyframes overlayFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
||||||
@keyframes overlayFadeOut { from { opacity: 1; } to { opacity: 0; } }
|
|
||||||
@keyframes modalFadeIn {
|
@keyframes modalFadeIn {
|
||||||
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
||||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(127, 29, 29, 0.2);
|
isolation: isolate;
|
||||||
backdrop-filter: blur(6px);
|
background: transparent;
|
||||||
-webkit-backdrop-filter: blur(6px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -11,6 +10,19 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tint + blur on ::before so opacity animations on .overlay cannot disable backdrop-filter. */
|
||||||
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: rgba(127, 29, 29, 0.28);
|
||||||
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -105,13 +117,8 @@
|
|||||||
color: #ef4444;
|
color: #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayIn { animation: overlayFadeIn 0.22s ease forwards; }
|
|
||||||
.overlayOut { animation: overlayFadeOut 0.22s ease forwards; }
|
|
||||||
.modalIn { animation: modalFadeIn 0.25s ease forwards; }
|
.modalIn { animation: modalFadeIn 0.25s ease forwards; }
|
||||||
.modalOut { animation: modalFadeOut 0.22s ease forwards; }
|
.modalOut { animation: modalFadeOut 0.22s ease forwards; }
|
||||||
|
|
||||||
@keyframes overlayFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
||||||
@keyframes overlayFadeOut { from { opacity: 1; } to { opacity: 0; } }
|
|
||||||
@keyframes modalFadeIn {
|
@keyframes modalFadeIn {
|
||||||
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
||||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
|
isolation: isolate;
|
||||||
z-index: 300;
|
z-index: 300;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -8,18 +9,20 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
background: rgba(15, 23, 42, 0.82);
|
background: rgba(15, 23, 42, 0.82);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
-webkit-backdrop-filter: blur(8px);
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayIn {
|
|
||||||
animation: fadeIn 0.2s ease forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlayOut {
|
|
||||||
animation: fadeOut 0.2s ease forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.closeBtn {
|
.closeBtn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(15, 23, 42, 0.25);
|
isolation: isolate;
|
||||||
backdrop-filter: blur(6px);
|
background: transparent;
|
||||||
-webkit-backdrop-filter: blur(6px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -11,8 +10,18 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayIn { animation: overlayFadeIn 0.22s ease forwards; }
|
/* Tint + blur on ::before so opacity animations on .overlay cannot disable backdrop-filter. */
|
||||||
.overlayOut { animation: overlayFadeOut 0.22s ease forwards; }
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: var(--modal-overlay-bg);
|
||||||
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -206,9 +215,6 @@
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes overlayFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
||||||
@keyframes overlayFadeOut { from { opacity: 1; } to { opacity: 0; } }
|
|
||||||
@keyframes modalFadeIn {
|
@keyframes modalFadeIn {
|
||||||
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
||||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(15, 23, 42, 0.35);
|
isolation: isolate;
|
||||||
backdrop-filter: blur(10px);
|
background: transparent;
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -11,13 +10,23 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tint + blur on ::before so opacity animations on .overlay cannot disable backdrop-filter. */
|
||||||
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: var(--modal-overlay-bg);
|
||||||
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.overlayNested {
|
.overlayNested {
|
||||||
z-index: 1010;
|
z-index: 1010;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayIn { animation: overlayFadeIn 0.22s ease forwards; }
|
|
||||||
.overlayOut { animation: overlayFadeOut 0.22s ease forwards; }
|
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
@@ -420,9 +429,6 @@
|
|||||||
color: #ef4444;
|
color: #ef4444;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes overlayFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
||||||
@keyframes overlayFadeOut { from { opacity: 1; } to { opacity: 0; } }
|
|
||||||
@keyframes modalFadeIn {
|
@keyframes modalFadeIn {
|
||||||
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
||||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||||
|
|||||||
@@ -393,6 +393,8 @@ const en = {
|
|||||||
'storeItems.loading': 'Loading store items...',
|
'storeItems.loading': 'Loading store items...',
|
||||||
'storeItems.empty': 'No store items yet. Use the + button to add products from your catalog.',
|
'storeItems.empty': 'No store items yet. Use the + button to add products from your catalog.',
|
||||||
'storeItems.emptyFiltered': 'No store items match your filters.',
|
'storeItems.emptyFiltered': 'No store items match your filters.',
|
||||||
|
'storeItems.pagination':
|
||||||
|
'Page {page} / {totalPages} · {perPage} per page · {total} total',
|
||||||
'storeItems.errorLoad': 'Unable to load store items.',
|
'storeItems.errorLoad': 'Unable to load store items.',
|
||||||
'storeItems.errorRemove': 'Unable to remove store item.',
|
'storeItems.errorRemove': 'Unable to remove store item.',
|
||||||
'storeItems.add': 'Add store items',
|
'storeItems.add': 'Add store items',
|
||||||
@@ -1490,6 +1492,8 @@ const fa: Record<MessageKey, string> = {
|
|||||||
'storeItems.loading': 'در حال بارگذاری اقلام فروشگاه...',
|
'storeItems.loading': 'در حال بارگذاری اقلام فروشگاه...',
|
||||||
'storeItems.empty': 'هنوز قلمی در فروشگاه نیست. با دکمه + از کاتالوگ اضافه کنید.',
|
'storeItems.empty': 'هنوز قلمی در فروشگاه نیست. با دکمه + از کاتالوگ اضافه کنید.',
|
||||||
'storeItems.emptyFiltered': 'هیچ قلمی با فیلترهای شما مطابقت ندارد.',
|
'storeItems.emptyFiltered': 'هیچ قلمی با فیلترهای شما مطابقت ندارد.',
|
||||||
|
'storeItems.pagination':
|
||||||
|
'صفحه {page} / {totalPages} · {perPage} در هر صفحه · {total} مجموع',
|
||||||
'storeItems.errorLoad': 'بارگذاری اقلام فروشگاه ممکن نشد.',
|
'storeItems.errorLoad': 'بارگذاری اقلام فروشگاه ممکن نشد.',
|
||||||
'storeItems.errorRemove': 'حذف قلم از فروشگاه ممکن نشد.',
|
'storeItems.errorRemove': 'حذف قلم از فروشگاه ممکن نشد.',
|
||||||
'storeItems.add': 'افزودن اقلام فروشگاه',
|
'storeItems.add': 'افزودن اقلام فروشگاه',
|
||||||
@@ -1504,7 +1508,7 @@ const fa: Record<MessageKey, string> = {
|
|||||||
'storeItems.card.festival': 'جشنواره',
|
'storeItems.card.festival': 'جشنواره',
|
||||||
'storeItems.card.variantOne': '۱ تنوع',
|
'storeItems.card.variantOne': '۱ تنوع',
|
||||||
'storeItems.card.variantMany': '{count} تنوع',
|
'storeItems.card.variantMany': '{count} تنوع',
|
||||||
'storeItems.card.edit': 'ویرایش اقلام فروشگاه',
|
'storeItems.card.edit': 'ویرایش محصول فروشگاه',
|
||||||
'storeItems.card.discount': 'تنظیم تخفیف',
|
'storeItems.card.discount': 'تنظیم تخفیف',
|
||||||
'storeItems.card.festivalPoints': 'امتیاز جشنواره',
|
'storeItems.card.festivalPoints': 'امتیاز جشنواره',
|
||||||
'storeItems.card.remove': 'حذف از فروشگاه',
|
'storeItems.card.remove': 'حذف از فروشگاه',
|
||||||
@@ -1543,7 +1547,7 @@ const fa: Record<MessageKey, string> = {
|
|||||||
'storeItems.create.errorVariations': 'بارگذاری تنوعهای محصول ممکن نشد.',
|
'storeItems.create.errorVariations': 'بارگذاری تنوعهای محصول ممکن نشد.',
|
||||||
'storeItems.create.errorCreate': 'ایجاد اقلام فروشگاه ممکن نشد.',
|
'storeItems.create.errorCreate': 'ایجاد اقلام فروشگاه ممکن نشد.',
|
||||||
'storeItems.price.contact': 'برای قیمت، تماس بگیرید',
|
'storeItems.price.contact': 'برای قیمت، تماس بگیرید',
|
||||||
'storeItems.edit.title': 'ویرایش اقلام فروشگاه',
|
'storeItems.edit.title': 'ویرایش محصول فروشگاه',
|
||||||
'storeItems.edit.save': 'ذخیره تغییرات',
|
'storeItems.edit.save': 'ذخیره تغییرات',
|
||||||
'storeItems.edit.saving': 'در حال ذخیره…',
|
'storeItems.edit.saving': 'در حال ذخیره…',
|
||||||
'storeItems.edit.errorSave': 'ذخیره اقلام فروشگاه ممکن نشد.',
|
'storeItems.edit.errorSave': 'ذخیره اقلام فروشگاه ممکن نشد.',
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
--glass-border: rgba(255, 255, 255, 0.7);
|
--glass-border: rgba(255, 255, 255, 0.7);
|
||||||
--glass-shadow: 0 8px 32px rgba(31, 38, 135, 0.08);
|
--glass-shadow: 0 8px 32px rgba(31, 38, 135, 0.08);
|
||||||
--blur-glass: 28px;
|
--blur-glass: 28px;
|
||||||
|
--modal-overlay-bg: rgba(15, 23, 42, 0.4);
|
||||||
|
--modal-overlay-blur: 12px;
|
||||||
--text-primary: #1e293b;
|
--text-primary: #1e293b;
|
||||||
--text-secondary: #64748b;
|
--text-secondary: #64748b;
|
||||||
--text-muted: #94a3b8;
|
--text-muted: #94a3b8;
|
||||||
|
|||||||
@@ -24,6 +24,39 @@
|
|||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 0 4px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto 1fr;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 38px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paginationMeta {
|
||||||
|
grid-column: 1;
|
||||||
|
justify-self: start;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 38px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 38px;
|
||||||
|
font-family: var(--font-ui);
|
||||||
|
transform: translateY(2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination > nav {
|
||||||
|
grid-column: 2;
|
||||||
|
justify-self: center;
|
||||||
|
height: 38px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.fabDock {
|
.fabDock {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset-inline-end: 32px;
|
inset-inline-end: 32px;
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useSearchParams } from 'react-router-dom'
|
||||||
import { Plus, RotateCcw, Search } from 'lucide-react'
|
import { Plus, RotateCcw, Search } from 'lucide-react'
|
||||||
import { Breadcrumbs } from '../components/Breadcrumbs'
|
import { Breadcrumbs } from '../components/Breadcrumbs'
|
||||||
import { ConfirmDeleteModal } from '../components/ConfirmDeleteModal'
|
import { ConfirmDeleteModal } from '../components/ConfirmDeleteModal'
|
||||||
import { CreateStoreItemsModal } from '../components/CreateStoreItemsModal'
|
import { CreateStoreItemsModal } from '../components/CreateStoreItemsModal'
|
||||||
import { EditStoreItemsModal } from '../components/EditStoreItemsModal'
|
import { EditStoreItemsModal } from '../components/EditStoreItemsModal'
|
||||||
|
import { Pagination } from '../components/Pagination'
|
||||||
import { PickStoreVariantModal } from '../components/PickStoreVariantModal'
|
import { PickStoreVariantModal } from '../components/PickStoreVariantModal'
|
||||||
import { ShoppingCartModal } from '../components/ShoppingCartModal'
|
import { ShoppingCartModal } from '../components/ShoppingCartModal'
|
||||||
import { StoreItemCard } from '../components/StoreItemCard'
|
import { StoreItemCard } from '../components/StoreItemCard'
|
||||||
@@ -15,7 +17,8 @@ import { useToast } from '../context/ToastContext'
|
|||||||
import { ApiError } from '../lib/api'
|
import { ApiError } from '../lib/api'
|
||||||
import {
|
import {
|
||||||
deleteStoreItemsByProduct,
|
deleteStoreItemsByProduct,
|
||||||
listStoreItems,
|
listAllStoreItems,
|
||||||
|
STORE_ITEMS_PER_PAGE,
|
||||||
type StoreItem,
|
type StoreItem,
|
||||||
} from '../services/storeItemService'
|
} from '../services/storeItemService'
|
||||||
import type { ShoppingCard } from '../services/shoppingCardService'
|
import type { ShoppingCard } from '../services/shoppingCardService'
|
||||||
@@ -37,6 +40,11 @@ import filterStyles from '../components/ListFiltersPanel.module.css'
|
|||||||
import pageStyles from '../components/PageContent.module.css'
|
import pageStyles from '../components/PageContent.module.css'
|
||||||
import styles from './StoreItemsPage.module.css'
|
import styles from './StoreItemsPage.module.css'
|
||||||
|
|
||||||
|
function readPage(searchParams: URLSearchParams) {
|
||||||
|
const pageRaw = Number(searchParams.get('page'))
|
||||||
|
return Number.isFinite(pageRaw) && pageRaw >= 1 ? Math.floor(pageRaw) : 1
|
||||||
|
}
|
||||||
|
|
||||||
export function StoreItemsPage() {
|
export function StoreItemsPage() {
|
||||||
return <StoreItemsPageContent />
|
return <StoreItemsPageContent />
|
||||||
}
|
}
|
||||||
@@ -44,6 +52,8 @@ export function StoreItemsPage() {
|
|||||||
function StoreItemsPageContent() {
|
function StoreItemsPageContent() {
|
||||||
const t = useT()
|
const t = useT()
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams()
|
||||||
|
const currentPage = readPage(searchParams)
|
||||||
const { itemCount, hasItems, addVariant, loadShoppingCard } = useDraftCart()
|
const { itemCount, hasItems, addVariant, loadShoppingCard } = useDraftCart()
|
||||||
const { showToast } = useToast()
|
const { showToast } = useToast()
|
||||||
const [items, setItems] = useState<StoreItem[]>([])
|
const [items, setItems] = useState<StoreItem[]>([])
|
||||||
@@ -97,6 +107,20 @@ function StoreItemsPageContent() {
|
|||||||
[listings, appliedFilters],
|
[listings, appliedFilters],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const totalListings = filteredListings.length
|
||||||
|
const totalPages = Math.max(1, Math.ceil(totalListings / STORE_ITEMS_PER_PAGE))
|
||||||
|
const safePage = Math.min(currentPage, totalPages)
|
||||||
|
const pagedListings = useMemo(() => {
|
||||||
|
const start = (safePage - 1) * STORE_ITEMS_PER_PAGE
|
||||||
|
return filteredListings.slice(start, start + STORE_ITEMS_PER_PAGE)
|
||||||
|
}, [filteredListings, safePage])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLoading && currentPage > totalPages) {
|
||||||
|
writePage(totalPages)
|
||||||
|
}
|
||||||
|
}, [isLoading, currentPage, totalPages])
|
||||||
|
|
||||||
const productItems = useMemo(() => {
|
const productItems = useMemo(() => {
|
||||||
const target = editTarget ?? discountTarget ?? festivalTarget
|
const target = editTarget ?? discountTarget ?? festivalTarget
|
||||||
if (!target) return []
|
if (!target) return []
|
||||||
@@ -108,6 +132,13 @@ function StoreItemsPageContent() {
|
|||||||
[listings],
|
[listings],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
function writePage(page: number) {
|
||||||
|
const next = new URLSearchParams(searchParams)
|
||||||
|
if (page > 1) next.set('page', String(page))
|
||||||
|
else next.delete('page')
|
||||||
|
setSearchParams(next, { replace: true })
|
||||||
|
}
|
||||||
|
|
||||||
function applyFilters() {
|
function applyFilters() {
|
||||||
setAppliedFilters({
|
setAppliedFilters({
|
||||||
name: draftName.trim(),
|
name: draftName.trim(),
|
||||||
@@ -115,6 +146,7 @@ function StoreItemsPageContent() {
|
|||||||
maxPrice: parseIrtInput(draftMaxPrice),
|
maxPrice: parseIrtInput(draftMaxPrice),
|
||||||
onlyDiscounted: draftOnlyDiscounted,
|
onlyDiscounted: draftOnlyDiscounted,
|
||||||
})
|
})
|
||||||
|
writePage(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearFilters() {
|
function clearFilters() {
|
||||||
@@ -123,6 +155,12 @@ function StoreItemsPageContent() {
|
|||||||
setDraftMaxPrice('')
|
setDraftMaxPrice('')
|
||||||
setDraftOnlyDiscounted(false)
|
setDraftOnlyDiscounted(false)
|
||||||
setAppliedFilters(EMPTY_STORE_LISTING_FILTERS)
|
setAppliedFilters(EMPTY_STORE_LISTING_FILTERS)
|
||||||
|
writePage(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
writePage(page)
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -136,8 +174,8 @@ function StoreItemsPageContent() {
|
|||||||
setError('')
|
setError('')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await listStoreItems(1, 50, signal)
|
const data = await listAllStoreItems(signal)
|
||||||
setItems(data.items)
|
setItems(data)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof DOMException && err.name === 'AbortError') return
|
if (err instanceof DOMException && err.name === 'AbortError') return
|
||||||
if (err instanceof ApiError) {
|
if (err instanceof ApiError) {
|
||||||
@@ -215,6 +253,12 @@ function StoreItemsPageContent() {
|
|||||||
await deleteStoreItemsByProduct(deleteTarget.productId)
|
await deleteStoreItemsByProduct(deleteTarget.productId)
|
||||||
setItems((prev) => prev.filter((item) => item.productId !== deleteTarget.productId))
|
setItems((prev) => prev.filter((item) => item.productId !== deleteTarget.productId))
|
||||||
setDeleteTarget(null)
|
setDeleteTarget(null)
|
||||||
|
|
||||||
|
const nextTotal = Math.max(0, totalListings - 1)
|
||||||
|
const nextTotalPages = Math.max(1, Math.ceil(nextTotal / STORE_ITEMS_PER_PAGE))
|
||||||
|
if (safePage > nextTotalPages) {
|
||||||
|
writePage(nextTotalPages)
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof ApiError) {
|
if (err instanceof ApiError) {
|
||||||
setError(err.message)
|
setError(err.message)
|
||||||
@@ -329,20 +373,40 @@ function StoreItemsPageContent() {
|
|||||||
) : filteredListings.length === 0 ? (
|
) : filteredListings.length === 0 ? (
|
||||||
<p className={styles.empty}>{t('storeItems.emptyFiltered')}</p>
|
<p className={styles.empty}>{t('storeItems.emptyFiltered')}</p>
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.grid}>
|
<>
|
||||||
{filteredListings.map((listing) => (
|
<div className={styles.grid}>
|
||||||
<StoreItemCard
|
{pagedListings.map((listing) => (
|
||||||
key={listing.productId}
|
<StoreItemCard
|
||||||
listing={listing}
|
key={listing.productId}
|
||||||
onOpen={openEdit}
|
listing={listing}
|
||||||
onEdit={openEdit}
|
onOpen={openEdit}
|
||||||
onDiscount={setDiscountTarget}
|
onEdit={openEdit}
|
||||||
onFestival={setFestivalTarget}
|
onDiscount={setDiscountTarget}
|
||||||
onRemove={setDeleteTarget}
|
onFestival={setFestivalTarget}
|
||||||
onAddToCart={handleAddToCart}
|
onRemove={setDeleteTarget}
|
||||||
|
onAddToCart={handleAddToCart}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.pagination}>
|
||||||
|
<div className={styles.paginationMeta}>
|
||||||
|
{t('storeItems.pagination', {
|
||||||
|
page: safePage,
|
||||||
|
totalPages,
|
||||||
|
perPage: STORE_ITEMS_PER_PAGE,
|
||||||
|
total: totalListings,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<Pagination
|
||||||
|
currentPage={safePage}
|
||||||
|
totalPages={totalPages}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
disabled={isLoading}
|
||||||
|
variant="inline"
|
||||||
/>
|
/>
|
||||||
))}
|
</div>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.fabDock}>
|
<div className={styles.fabDock}>
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { apiRequest } from '../lib/api'
|
import { apiRequest } from '../lib/api'
|
||||||
import { getActiveBusinessId } from '../lib/businessContext'
|
import { getActiveBusinessId } from '../lib/businessContext'
|
||||||
|
|
||||||
|
/** Product cards per page on My Store Items (grid). */
|
||||||
|
export const STORE_ITEMS_PER_PAGE = 24
|
||||||
|
|
||||||
export interface StoreItemSelection {
|
export interface StoreItemSelection {
|
||||||
variationId: string
|
variationId: string
|
||||||
variationName: string
|
variationName: string
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(15, 23, 42, 0.25);
|
isolation: isolate;
|
||||||
backdrop-filter: blur(6px);
|
background: transparent;
|
||||||
-webkit-backdrop-filter: blur(6px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -11,13 +10,23 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tint + blur on ::before so opacity animations on .overlay cannot disable backdrop-filter. */
|
||||||
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: var(--modal-overlay-bg);
|
||||||
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.overlayNested {
|
.overlayNested {
|
||||||
z-index: 210;
|
z-index: 210;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayIn { animation: overlayFadeIn 0.22s ease forwards; }
|
|
||||||
.overlayOut { animation: overlayFadeOut 0.22s ease forwards; }
|
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
@@ -412,9 +421,6 @@
|
|||||||
color: #ef4444;
|
color: #ef4444;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes overlayFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
||||||
@keyframes overlayFadeOut { from { opacity: 1; } to { opacity: 0; } }
|
|
||||||
@keyframes modalFadeIn {
|
@keyframes modalFadeIn {
|
||||||
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
||||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||||
|
|||||||
@@ -1,16 +1,28 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(127, 29, 29, 0.28);
|
isolation: isolate;
|
||||||
backdrop-filter: blur(10px);
|
background: transparent;
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
z-index: 300;
|
z-index: 200;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tint + blur on ::before so opacity animations on .overlay cannot disable backdrop-filter. */
|
||||||
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: rgba(127, 29, 29, 0.28);
|
||||||
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -105,13 +117,8 @@
|
|||||||
color: #ef4444;
|
color: #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayIn { animation: overlayFadeIn 0.22s ease forwards; }
|
|
||||||
.overlayOut { animation: overlayFadeOut 0.22s ease forwards; }
|
|
||||||
.modalIn { animation: modalFadeIn 0.25s ease forwards; }
|
.modalIn { animation: modalFadeIn 0.25s ease forwards; }
|
||||||
.modalOut { animation: modalFadeOut 0.22s ease forwards; }
|
.modalOut { animation: modalFadeOut 0.22s ease forwards; }
|
||||||
|
|
||||||
@keyframes overlayFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
||||||
@keyframes overlayFadeOut { from { opacity: 1; } to { opacity: 0; } }
|
|
||||||
@keyframes modalFadeIn {
|
@keyframes modalFadeIn {
|
||||||
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
||||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(15, 23, 42, 0.32);
|
isolation: isolate;
|
||||||
backdrop-filter: blur(10px);
|
background: transparent;
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -11,6 +10,19 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tint + blur on ::before so opacity animations on .overlay cannot disable backdrop-filter. */
|
||||||
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: var(--modal-overlay-bg);
|
||||||
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 520px;
|
max-width: 520px;
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
--radius: 16px;
|
--radius: 16px;
|
||||||
--radius-sm: 12px;
|
--radius-sm: 12px;
|
||||||
--blur-glass: 28px;
|
--blur-glass: 28px;
|
||||||
|
--modal-overlay-bg: rgba(15, 23, 42, 0.4);
|
||||||
|
--modal-overlay-blur: 12px;
|
||||||
--select-arrow-size: 16px;
|
--select-arrow-size: 16px;
|
||||||
--select-arrow-offset: 12px;
|
--select-arrow-offset: 12px;
|
||||||
--select-padding-end: 2.5rem;
|
--select-padding-end: 2.5rem;
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
--glass-border: rgba(255, 255, 255, 0.7);
|
--glass-border: rgba(255, 255, 255, 0.7);
|
||||||
--glass-shadow: 0 8px 32px rgba(31, 38, 135, 0.08);
|
--glass-shadow: 0 8px 32px rgba(31, 38, 135, 0.08);
|
||||||
--blur-glass: 28px;
|
--blur-glass: 28px;
|
||||||
|
--modal-overlay-bg: rgba(15, 23, 42, 0.4);
|
||||||
|
--modal-overlay-blur: 12px;
|
||||||
--text-primary: #1e293b;
|
--text-primary: #1e293b;
|
||||||
--text-secondary: #64748b;
|
--text-secondary: #64748b;
|
||||||
--text-muted: #94a3b8;
|
--text-muted: #94a3b8;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(15, 23, 42, 0.25);
|
isolation: isolate;
|
||||||
backdrop-filter: blur(8px);
|
background: transparent;
|
||||||
-webkit-backdrop-filter: blur(8px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -11,6 +10,19 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tint + blur on ::before so opacity animations on .overlay cannot disable backdrop-filter. */
|
||||||
|
.overlay::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: var(--modal-overlay-bg);
|
||||||
|
backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-overlay-blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -245,13 +257,8 @@
|
|||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayIn { animation: overlayFadeIn 0.22s ease forwards; }
|
|
||||||
.overlayOut { animation: overlayFadeOut 0.22s ease forwards; }
|
|
||||||
.modalIn { animation: modalFadeIn 0.25s ease forwards; }
|
.modalIn { animation: modalFadeIn 0.25s ease forwards; }
|
||||||
.modalOut { animation: modalFadeOut 0.22s ease forwards; }
|
.modalOut { animation: modalFadeOut 0.22s ease forwards; }
|
||||||
|
|
||||||
@keyframes overlayFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
||||||
@keyframes overlayFadeOut { from { opacity: 1; } to { opacity: 0; } }
|
|
||||||
@keyframes modalFadeIn {
|
@keyframes modalFadeIn {
|
||||||
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
||||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||||
|
|||||||
Reference in New Issue
Block a user