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 {