From 4d365a373bebd105e805bb2c9faa5f1dabb3f502 Mon Sep 17 00:00:00 2001 From: NotBigGhost Date: Fri, 26 Jun 2026 16:56:12 +0300 Subject: [PATCH] =?UTF-8?q?UI:=20=D1=80=D0=B0=D0=BD=D0=B4=D0=BE=D0=BC=20?= =?UTF-8?q?=D1=84=D1=80=D0=B0=D0=BA=D1=86=D0=B8=D0=B8=20=E2=80=94=20=D1=83?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D1=8C=20=C2=AB=D1=81=D0=BB=D1=83=D1=87=D0=B0=D0=B9=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=C2=BB,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=B0=D0=BD=D0=B8=D0=BC=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F-=D0=B1=D0=B0=D1=80=D0=B0=D0=B1=D0=B0=D0=BD=20=D0=BD?= =?UTF-8?q?=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8=D0=B9=20=D0=B2=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- frontend/src/pages/CreateMatchPage.tsx | 58 ++++++++++++++++++-------- frontend/src/styles/global.css | 21 ++++++++++ 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/frontend/src/pages/CreateMatchPage.tsx b/frontend/src/pages/CreateMatchPage.tsx index c2638bc..ff7de5f 100644 --- a/frontend/src/pages/CreateMatchPage.tsx +++ b/frontend/src/pages/CreateMatchPage.tsx @@ -34,6 +34,8 @@ export function CreateMatchPage() { const [rows, setRows] = useState([emptyDraft(), emptyDraft()]); const [error, setError] = useState(null); + // Анимация-«барабан» рандома: для крутящейся строки — текущее мелькающее название. + const [spin, setSpin] = useState>({}); if (lm || lf) return ; if (!groupId) return
Нет активной группы.
; @@ -51,13 +53,25 @@ export function CreateMatchPage() { const removeRow = (idx: number) => setRows((rs) => rs.filter((_, i) => i !== idx)); const randomizeRow = async (idx: number) => { + const pool = factions ?? []; + if (pool.length === 0) return; + // Запускаем «барабан»: быстро мелькаем названиями фракций. + const ticker = setInterval(() => { + const name = pool[Math.floor(Math.random() * pool.length)].name_ru; + setSpin((s) => ({ ...s, [idx]: name })); + }, 60); try { const faction = await randomize.mutateAsync({ group_id: groupId, exclude_faction_ids: takenFactionIds(idx), }); + await new Promise((r) => setTimeout(r, 700)); // даём барабану покрутиться + clearInterval(ticker); + setSpin((s) => ({ ...s, [idx]: null })); update(idx, { faction_id: faction.id, was_random: true }); } catch (e) { + clearInterval(ticker); + setSpin((s) => ({ ...s, [idx]: null })); toast.show(e instanceof ApiError ? e.message : "Ошибка рандома"); } }; @@ -137,28 +151,36 @@ export function CreateMatchPage() {
- + update(idx, { + faction_id: e.target.value ? Number(e.target.value) : "", + was_random: false, + }) + } + style={{ flex: 1 }} + > + + {(factions ?? []).map((f) => ( + + ))} + + )} +
- {row.was_random && row.faction_id !== "" && 🎲 случайная} ))} diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css index 21b0ba5..6bfb96a 100644 --- a/frontend/src/styles/global.css +++ b/frontend/src/styles/global.css @@ -92,6 +92,27 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent); } .error-text { color: var(--danger); font-size: 13px; } +/* «Барабан» рандома фракции — мелькающее название вместо select */ +.faction-spin { + display: flex; + align-items: center; + justify-content: center; + padding: 10px 12px; + border: 1px solid var(--accent); + border-radius: var(--radius-sm); + background: var(--surface-2); + color: var(--text); + font-weight: 700; + white-space: nowrap; + overflow: hidden; + animation: faction-roll 0.45s ease-in-out infinite; +} +@keyframes faction-roll { + 0% { opacity: 0.5; transform: translateY(-3px); } + 50% { opacity: 1; transform: translateY(0); } + 100% { opacity: 0.5; transform: translateY(3px); } +} + /* Тумблер-переключатель (вкл/выкл) */ .switch { position: relative; display: inline-block; width: 44px; height: 26px; flex: 0 0 auto; } .switch input {