Restructure views and use Telegram native action button
This commit is contained in:
parent
90c739d5ee
commit
41dbaa66f4
3 changed files with 113 additions and 48 deletions
|
|
@ -12,6 +12,8 @@
|
||||||
const state = {
|
const state = {
|
||||||
kind: "income",
|
kind: "income",
|
||||||
board: "all",
|
board: "all",
|
||||||
|
currentView: "dashboardView",
|
||||||
|
previousView: "dashboardView",
|
||||||
categoryFilter: "",
|
categoryFilter: "",
|
||||||
summary: null,
|
summary: null,
|
||||||
config: null,
|
config: null,
|
||||||
|
|
@ -24,17 +26,19 @@
|
||||||
const $ = (selector) => document.querySelector(selector);
|
const $ = (selector) => document.querySelector(selector);
|
||||||
const money = new Intl.NumberFormat("ru-RU", { style: "currency", currency: "RUB", maximumFractionDigits: 2 });
|
const money = new Intl.NumberFormat("ru-RU", { style: "currency", currency: "RUB", maximumFractionDigits: 2 });
|
||||||
|
|
||||||
|
function safeTelegram(action) {
|
||||||
|
try { action(); } catch (error) { console.warn("Telegram WebApp method failed", error); }
|
||||||
|
}
|
||||||
|
|
||||||
function initTelegram() {
|
function initTelegram() {
|
||||||
if (!tg) return;
|
if (!tg) return;
|
||||||
const safely = (action) => {
|
safeTelegram(() => tg.ready());
|
||||||
try { action(); } catch (error) { console.warn("Telegram WebApp method failed", error); }
|
safeTelegram(() => tg.expand());
|
||||||
};
|
safeTelegram(() => tg.enableClosingConfirmation?.());
|
||||||
safely(() => tg.ready());
|
if (tg.isVersionAtLeast?.("6.1")) safeTelegram(() => tg.setBackgroundColor("#111111"));
|
||||||
safely(() => tg.expand());
|
if (tg.isVersionAtLeast?.("6.9")) safeTelegram(() => tg.setHeaderColor("#111111"));
|
||||||
safely(() => tg.enableClosingConfirmation?.());
|
if (tg.isVersionAtLeast?.("7.7")) safeTelegram(() => tg.disableVerticalSwipes());
|
||||||
if (tg.isVersionAtLeast?.("6.1")) safely(() => tg.setBackgroundColor("#111111"));
|
if (tg.isVersionAtLeast?.("7.10")) safeTelegram(() => tg.setBottomBarColor("#111111"));
|
||||||
if (tg.isVersionAtLeast?.("6.9")) safely(() => tg.setHeaderColor("#111111"));
|
|
||||||
if (tg.isVersionAtLeast?.("7.10")) safely(() => tg.setBottomBarColor("#111111"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showToast(message) {
|
function showToast(message) {
|
||||||
|
|
@ -54,15 +58,59 @@
|
||||||
|
|
||||||
function bindNavigation() {
|
function bindNavigation() {
|
||||||
document.querySelectorAll(".bottom-nav button").forEach((button) => {
|
document.querySelectorAll(".bottom-nav button").forEach((button) => {
|
||||||
button.addEventListener("click", () => {
|
button.addEventListener("click", () => showView(button.dataset.view));
|
||||||
document.querySelectorAll(".bottom-nav button, .view").forEach((item) => item.classList.remove("active"));
|
|
||||||
button.classList.add("active");
|
|
||||||
$(`#${button.dataset.view}`).classList.add("active");
|
|
||||||
if (button.dataset.view === "leaderboardView") loadLeaderboard();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showView(viewId) {
|
||||||
|
document.querySelectorAll(".bottom-nav button, .view").forEach((item) => item.classList.remove("active"));
|
||||||
|
$(`#${viewId}`).classList.add("active");
|
||||||
|
document.querySelector(`.bottom-nav button[data-view="${viewId}"]`)?.classList.add("active");
|
||||||
|
state.currentView = viewId;
|
||||||
|
document.body.classList.toggle("form-open", viewId === "addView");
|
||||||
|
updateNativeControls();
|
||||||
|
window.scrollTo({ top: 0, behavior: "auto" });
|
||||||
|
if (viewId === "leaderboardView") loadLeaderboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAddView() {
|
||||||
|
if (state.currentView !== "addView") state.previousView = state.currentView;
|
||||||
|
showView("addView");
|
||||||
|
window.setTimeout(() => $("#amount").focus(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAddView() {
|
||||||
|
showView(state.previousView || "dashboardView");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupNativeControls() {
|
||||||
|
const isTelegramClient = Boolean(tg?.platform && tg.platform !== "unknown");
|
||||||
|
if (!isTelegramClient || !tg?.MainButton) return;
|
||||||
|
safeTelegram(() => tg.MainButton.onClick(() => {
|
||||||
|
if (state.currentView === "addView") {
|
||||||
|
$("#transactionForm").requestSubmit();
|
||||||
|
} else {
|
||||||
|
openAddView();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
safeTelegram(() => tg.BackButton?.onClick(closeAddView));
|
||||||
|
updateNativeControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateNativeControls() {
|
||||||
|
const isTelegramClient = Boolean(tg?.platform && tg.platform !== "unknown");
|
||||||
|
if (!isTelegramClient || !tg?.MainButton) return;
|
||||||
|
const isForm = state.currentView === "addView";
|
||||||
|
safeTelegram(() => tg.MainButton.setParams({
|
||||||
|
text: isForm ? "Сохранить операцию" : "Добавить запись",
|
||||||
|
color: "#f0d28a",
|
||||||
|
text_color: "#17130b",
|
||||||
|
is_active: true,
|
||||||
|
is_visible: true,
|
||||||
|
}));
|
||||||
|
safeTelegram(() => isForm ? tg.BackButton?.show() : tg.BackButton?.hide());
|
||||||
|
}
|
||||||
|
|
||||||
function fillCategories() {
|
function fillCategories() {
|
||||||
const select = $("#category");
|
const select = $("#category");
|
||||||
const items = [...new Set([...categories[state.kind], ...state.customCategories[state.kind]])];
|
const items = [...new Set([...categories[state.kind], ...state.customCategories[state.kind]])];
|
||||||
|
|
@ -143,10 +191,12 @@
|
||||||
}
|
}
|
||||||
if (!canSendViaTelegram()) return false;
|
if (!canSendViaTelegram()) return false;
|
||||||
try {
|
try {
|
||||||
|
safeTelegram(() => tg.MainButton?.showProgress());
|
||||||
tg.HapticFeedback?.notificationOccurred("success");
|
tg.HapticFeedback?.notificationOccurred("success");
|
||||||
tg.sendData(encoded);
|
tg.sendData(encoded);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
safeTelegram(() => tg.MainButton?.hideProgress());
|
||||||
console.error("Telegram sendData failed", error);
|
console.error("Telegram sendData failed", error);
|
||||||
showToast("Telegram не принял запись. Обновите приложение и попробуйте снова");
|
showToast("Telegram не принял запись. Обновите приложение и попробуйте снова");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -190,9 +240,6 @@
|
||||||
$("#balanceValue").textContent = money.format(summary.balance_cents / 100);
|
$("#balanceValue").textContent = money.format(summary.balance_cents / 100);
|
||||||
$("#incomeValue").textContent = money.format(summary.income_cents / 100);
|
$("#incomeValue").textContent = money.format(summary.income_cents / 100);
|
||||||
$("#expenseValue").textContent = money.format(summary.expense_cents / 100);
|
$("#expenseValue").textContent = money.format(summary.expense_cents / 100);
|
||||||
const percent = Number(summary.saved_percent);
|
|
||||||
$("#savedPercent").textContent = `${percent.toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`;
|
|
||||||
$("#savingProgress").style.width = `${Math.max(0, Math.min(100, percent))}%`;
|
|
||||||
renderCategories(summary.categories || []);
|
renderCategories(summary.categories || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -369,6 +416,7 @@
|
||||||
initTelegram();
|
initTelegram();
|
||||||
setIdentity();
|
setIdentity();
|
||||||
bindNavigation();
|
bindNavigation();
|
||||||
|
setupNativeControls();
|
||||||
bindForm();
|
bindForm();
|
||||||
bindLeaderboard();
|
bindLeaderboard();
|
||||||
fillCategories();
|
fillCategories();
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@
|
||||||
<meta name="color-scheme" content="dark" />
|
<meta name="color-scheme" content="dark" />
|
||||||
<meta name="theme-color" content="#111111" />
|
<meta name="theme-color" content="#111111" />
|
||||||
<title>Пожитки</title>
|
<title>Пожитки</title>
|
||||||
<link rel="stylesheet" href="./styles.css?v=6" />
|
<link rel="stylesheet" href="./styles.css?v=7" />
|
||||||
<script src="./telegram-web-app.js"></script>
|
<script src="./telegram-web-app.js"></script>
|
||||||
<script src="./app.js?v=4" defer></script>
|
<script src="./app.js?v=7" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
|
|
@ -24,25 +24,28 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section id="dashboardView" class="view active" aria-labelledby="dashboardTab">
|
<section id="dashboardView" class="view active" aria-labelledby="dashboardTab">
|
||||||
<article class="hero-card">
|
<div class="dashboard-summary">
|
||||||
<p>Накоплено</p>
|
<article class="hero-card">
|
||||||
<strong id="balanceValue">0 ₽</strong>
|
<p>Текущий баланс</p>
|
||||||
<div class="saving-row">
|
<strong id="balanceValue">0 ₽</strong>
|
||||||
<span id="savedPercent">0%</span>
|
</article>
|
||||||
<div class="progress"><i id="savingProgress"></i></div>
|
|
||||||
</div>
|
|
||||||
<small>от всех поступлений</small>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<div class="metric-grid">
|
<div class="metric-grid">
|
||||||
<article class="metric income">
|
<article class="metric income">
|
||||||
<span class="metric-icon">↗</span>
|
<span class="metric-icon">↗</span>
|
||||||
<div><small>Поступления</small><strong id="incomeValue">0 ₽</strong></div>
|
<div><small>Поступления</small><strong id="incomeValue">0 ₽</strong></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="metric expense">
|
<article class="metric expense">
|
||||||
<span class="metric-icon">↘</span>
|
<span class="metric-icon">↘</span>
|
||||||
<div><small>Расходы</small><strong id="expenseValue">0 ₽</strong></div>
|
<div><small>Расходы</small><strong id="expenseValue">0 ₽</strong></div>
|
||||||
</article>
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="historyView" class="view" aria-labelledby="historyTab">
|
||||||
|
<div class="section-title history-page-heading">
|
||||||
|
<div><p class="eyebrow">Движение денег</p><h2>История</h2></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
|
|
@ -62,8 +65,8 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="addView" class="view" aria-labelledby="addTab">
|
<section id="addView" class="view" aria-labelledby="addHeading">
|
||||||
<div class="section-title add-heading">
|
<div id="addHeading" class="section-title add-heading">
|
||||||
<div><p class="eyebrow">Новая запись</p><h2>Движение денег</h2></div>
|
<div><p class="eyebrow">Новая запись</p><h2>Движение денег</h2></div>
|
||||||
</div>
|
</div>
|
||||||
<form id="transactionForm" class="transaction-form">
|
<form id="transactionForm" class="transaction-form">
|
||||||
|
|
@ -93,7 +96,6 @@
|
||||||
<input id="note" name="note" maxlength="160" placeholder="За что или откуда" />
|
<input id="note" name="note" maxlength="160" placeholder="За что или откуда" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<button class="primary-button" type="submit">Сохранить через Telegram</button>
|
|
||||||
<p class="form-hint">Telegram передаст запись боту вместе с вашим подтверждённым ID.</p>
|
<p class="form-hint">Telegram передаст запись боту вместе с вашим подтверждённым ID.</p>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -120,7 +122,7 @@
|
||||||
|
|
||||||
<nav class="bottom-nav" aria-label="Основная навигация">
|
<nav class="bottom-nav" aria-label="Основная навигация">
|
||||||
<button id="dashboardTab" class="active" data-view="dashboardView"><span>◫</span>Обзор</button>
|
<button id="dashboardTab" class="active" data-view="dashboardView"><span>◫</span>Обзор</button>
|
||||||
<button id="addTab" data-view="addView"><span class="add-mark">+</span>Добавить</button>
|
<button id="historyTab" data-view="historyView"><span>☷</span>История</button>
|
||||||
<button id="leaderboardTab" data-view="leaderboardView"><span>♜</span>Рейтинг</button>
|
<button id="leaderboardTab" data-view="leaderboardView"><span>♜</span>Рейтинг</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,13 @@
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body { margin: 0; min-height: 100%; background: var(--bg); }
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
background: var(--bg);
|
||||||
|
overscroll-behavior: none;
|
||||||
|
overscroll-behavior-y: none;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -28,6 +34,7 @@ body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
padding: max(14px, env(safe-area-inset-top)) 18px calc(96px + env(safe-area-inset-bottom));
|
padding: max(14px, env(safe-area-inset-top)) 18px calc(96px + env(safe-area-inset-bottom));
|
||||||
|
overflow-x: hidden;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +53,7 @@ body::before {
|
||||||
|
|
||||||
body::after {
|
body::after {
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
background: rgba(0, 0, 0, .16);
|
background: rgba(0, 0, 0, .2);
|
||||||
}
|
}
|
||||||
|
|
||||||
button, input, select { font: inherit; color: inherit; }
|
button, input, select { font: inherit; color: inherit; }
|
||||||
|
|
@ -63,7 +70,7 @@ button:focus { outline: none; }
|
||||||
button:focus-visible { outline: 2px solid rgba(255, 255, 255, .72); outline-offset: 2px; }
|
button:focus-visible { outline: 2px solid rgba(255, 255, 255, .72); outline-offset: 2px; }
|
||||||
button:active { filter: brightness(.88); }
|
button:active { filter: brightness(.88); }
|
||||||
|
|
||||||
.shell { width: min(100%, 560px); margin: 0 auto; }
|
.shell { display: flex; flex-direction: column; width: min(100%, 560px); min-height: calc(var(--tg-viewport-stable-height, 100dvh) - 110px); margin: 0 auto; }
|
||||||
.topbar { display: flex; align-items: center; justify-content: space-between; margin: 6px 2px 26px; text-shadow: 0 2px 14px rgba(0, 0, 0, .55); }
|
.topbar { display: flex; align-items: center; justify-content: space-between; margin: 6px 2px 26px; text-shadow: 0 2px 14px rgba(0, 0, 0, .55); }
|
||||||
.eyebrow { margin: 0 0 3px; text-transform: uppercase; letter-spacing: .12em; font-size: 10px; font-weight: 750; color: var(--muted); }
|
.eyebrow { margin: 0 0 3px; text-transform: uppercase; letter-spacing: .12em; font-size: 10px; font-weight: 750; color: var(--muted); }
|
||||||
h1, h2, p { margin-top: 0; }
|
h1, h2, p { margin-top: 0; }
|
||||||
|
|
@ -72,11 +79,16 @@ h2 { margin: 0; font-size: 23px; letter-spacing: -.025em; }
|
||||||
.user-chip { display: flex; align-items: center; gap: 8px; max-width: 48%; padding: 5px 11px 5px 5px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); font-size: 12px; font-weight: 650; white-space: nowrap; overflow: hidden; }
|
.user-chip { display: flex; align-items: center; gap: 8px; max-width: 48%; padding: 5px 11px 5px 5px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); font-size: 12px; font-weight: 650; white-space: nowrap; overflow: hidden; }
|
||||||
.mini-avatar { display: grid; flex: 0 0 auto; place-items: center; width: 29px; height: 29px; border-radius: 50%; background: rgba(0, 0, 0, .28); color: var(--text); }
|
.mini-avatar { display: grid; flex: 0 0 auto; place-items: center; width: 29px; height: 29px; border-radius: 50%; background: rgba(0, 0, 0, .28); color: var(--text); }
|
||||||
|
|
||||||
.view { display: none; padding-top: clamp(120px, 17vh, 175px); }
|
.view { display: none; padding-top: 0; }
|
||||||
.view.active { display: block; }
|
.view.active { display: block; }
|
||||||
|
#dashboardView.active { display: flex; flex: 1; flex-direction: column; justify-content: flex-end; }
|
||||||
|
.dashboard-summary { width: 100%; margin-top: auto; }
|
||||||
|
|
||||||
.hero-card {
|
.hero-card {
|
||||||
min-height: 210px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
min-height: 190px;
|
||||||
padding: 27px;
|
padding: 27px;
|
||||||
border-radius: 29px;
|
border-radius: 29px;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
|
|
@ -91,6 +103,8 @@ h2 { margin: 0; font-size: 23px; letter-spacing: -.025em; }
|
||||||
.progress { flex: 1; height: 7px; overflow: hidden; border-radius: 99px; background: rgba(255,255,255,.2); }
|
.progress { flex: 1; height: 7px; overflow: hidden; border-radius: 99px; background: rgba(255,255,255,.2); }
|
||||||
.progress i { display: block; width: 0; height: 100%; border-radius: inherit; background: #e8d487; transition: width .4s ease; }
|
.progress i { display: block; width: 0; height: 100%; border-radius: inherit; background: #e8d487; transition: width .4s ease; }
|
||||||
.hero-card small { display: block; margin-top: 7px; opacity: .55; font-size: 11px; }
|
.hero-card small { display: block; margin-top: 7px; opacity: .55; font-size: 11px; }
|
||||||
|
.dashboard-summary .hero-card strong { margin-bottom: 0; }
|
||||||
|
.dashboard-summary .metric-grid { margin-bottom: 0; }
|
||||||
|
|
||||||
.metric-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; margin: 12px 0 28px; }
|
.metric-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; margin: 12px 0 28px; }
|
||||||
.metric { display: flex; align-items: center; gap: 11px; min-width: 0; padding: 17px 15px; border: 1px solid var(--line); border-radius: 20px; background: var(--surface); }
|
.metric { display: flex; align-items: center; gap: 11px; min-width: 0; padding: 17px 15px; border: 1px solid var(--line); border-radius: 20px; background: var(--surface); }
|
||||||
|
|
@ -113,6 +127,7 @@ h2 { margin: 0; font-size: 23px; letter-spacing: -.025em; }
|
||||||
.category-bar i { display: block; height: 100%; background: var(--accent); }
|
.category-bar i { display: block; height: 100%; background: var(--accent); }
|
||||||
.category-item.expense-item .category-bar i { background: var(--danger); }
|
.category-item.expense-item .category-bar i { background: var(--danger); }
|
||||||
.history-heading { margin-top: 28px; }
|
.history-heading { margin-top: 28px; }
|
||||||
|
.history-page-heading { margin: 5px 3px 24px; }
|
||||||
.transaction-list { overflow: hidden; border: 1px solid var(--line); border-radius: 21px; background: var(--surface); }
|
.transaction-list { overflow: hidden; border: 1px solid var(--line); border-radius: 21px; background: var(--surface); }
|
||||||
.transaction-row { display: grid; grid-template-columns: 40px 1fr auto; align-items: center; gap: 11px; padding: 13px 14px; border-bottom: 1px solid var(--line); }
|
.transaction-row { display: grid; grid-template-columns: 40px 1fr auto; align-items: center; gap: 11px; padding: 13px 14px; border-bottom: 1px solid var(--line); }
|
||||||
.transaction-row:last-child { border-bottom: 0; }
|
.transaction-row:last-child { border-bottom: 0; }
|
||||||
|
|
@ -168,7 +183,7 @@ h2 { margin: 0; font-size: 23px; letter-spacing: -.025em; }
|
||||||
.bottom-nav button { display: grid; place-items: center; gap: 2px; padding: 3px; border: 0; background: transparent; color: var(--muted); font-size: 9px; font-weight: 650; }
|
.bottom-nav button { display: grid; place-items: center; gap: 2px; padding: 3px; border: 0; background: transparent; color: var(--muted); font-size: 9px; font-weight: 650; }
|
||||||
.bottom-nav button > span { font-size: 22px; line-height: 30px; }
|
.bottom-nav button > span { font-size: 22px; line-height: 30px; }
|
||||||
.bottom-nav button.active { color: var(--accent); }
|
.bottom-nav button.active { color: var(--accent); }
|
||||||
.bottom-nav .add-mark { display: grid; place-items: center; width: 42px; height: 32px; border-radius: 12px; background: var(--accent); color: var(--accent-text); }
|
.form-open .bottom-nav { display: none; }
|
||||||
.toast { position: fixed; z-index: 20; left: 50%; bottom: calc(92px + env(safe-area-inset-bottom)); max-width: calc(100% - 36px); padding: 11px 16px; border: 1px solid var(--line); border-radius: 12px; transform: translate(-50%, 20px); background: rgba(0, 0, 0, .62); color: var(--text); opacity: 0; pointer-events: none; transition: .2s ease; font-size: 12px; box-shadow: var(--shadow); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); }
|
.toast { position: fixed; z-index: 20; left: 50%; bottom: calc(92px + env(safe-area-inset-bottom)); max-width: calc(100% - 36px); padding: 11px 16px; border: 1px solid var(--line); border-radius: 12px; transform: translate(-50%, 20px); background: rgba(0, 0, 0, .62); color: var(--text); opacity: 0; pointer-events: none; transition: .2s ease; font-size: 12px; box-shadow: var(--shadow); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); }
|
||||||
.toast.show { opacity: 1; transform: translate(-50%, 0); }
|
.toast.show { opacity: 1; transform: translate(-50%, 0); }
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue