feat: add toolbar action to remove existing shot

This commit is contained in:
2026-09-09 13:42:22 +09:00
parent 680cb21c80
commit 43e34b31ce
3 changed files with 12 additions and 1 deletions
+9
View File
@@ -321,6 +321,13 @@ function addShootForOwner() {
if (play === state.play) { state = workingState; renderUi(); document.querySelector('#status').textContent = hasShoot(state.play) ? '슛 행동은 전술당 1개만 가능합니다' : '슛은 마지막 단계의 공 소유 선수만 가능합니다'; return; }
state = workingState; const track = play.sequences[sequenceIndex].tracks.find((candidate) => candidate.playerId === ownerId); updatePlay(play, '슛 행동 추가', track.actions.length - 1);
}
function removeShootAction() {
const shot = state.play.sequences.flatMap((sequence, sequenceIndex) => sequence.tracks.flatMap((track) => track.actions.map((action, actionIndex) => ({ action, actionIndex, playerId: track.playerId, sequenceIndex })))).find(({ action }) => action.type === 'shoot');
if (!shot) return;
const play = removeAction(state.play, shot.sequenceIndex, shot.playerId, shot.actionIndex);
if (play === state.play) return;
setState({ ...commitActionEdit(state, play, shot.sequenceIndex, shot.playerId), selectedAction: -1 }, '슛 행동 삭제');
}
function renderUi() {
const readOnly = currentTeam?.role === 'viewer';
@@ -356,6 +363,7 @@ function renderUi() {
document.querySelector('#delete-action').disabled = state.mode === 'start' || state.selectedAction < 0;
const shootButton = document.querySelector('#shoot-action'); shootButton.hidden = selectedPlayer?.id !== finalOwnerId;
shootButton.disabled = playHasShoot || state.mode === 'start' || state.selectedSequence !== state.play.sequences.length - 1;
const removeShootButton = document.querySelector('#remove-shoot-action'); removeShootButton.hidden = !playHasShoot;
const passButton = document.querySelector('[data-mode="pass"]'); const canPass = selectedPlayer?.team === 'offense' && selectedPlayer.id === finalOwnerId;
passButton.hidden = !canPass; passButton.disabled = playHasShoot || !canPass;
document.querySelector('#add-sequence').disabled = playHasShoot;
@@ -454,6 +462,7 @@ app.addEventListener('click', (event) => {
if (event.target.closest('#save-play')) { saveCurrentPlay(); return; }
if (event.target.closest('#load-play')) { loadSelectedPlay(); return; }
if (event.target.closest('#shoot-action')) { addShootForOwner(); return; }
if (event.target.closest('#remove-shoot-action')) { removeShootAction(); return; }
if (event.target.closest('#undo')) { const next = editorHistory.undo(state); if (next !== state) setState(next, '실행 취소', false); return; }
if (event.target.closest('#redo')) { const next = editorHistory.redo(state); if (next !== state) setState(next, '다시 실행', false); return; }
if (event.target.closest('#set-ball-owner')) { const selectedPlayer = state.play.players.find((player) => player.id === state.selectedPlayerId); if (selectedPlayer?.team === 'offense') { const play = setSequenceBallOwner(state.play, state.selectedSequence, selectedPlayer.id); if (play === state.play) document.querySelector('#status').textContent = hasShoot(state.play) ? '슛 행동을 먼저 삭제하세요' : '먼저 패스 행동을 삭제하세요'; else setState({ ...state, play, mode: 'move', selectedAction: -1 }, `Sequence ${state.selectedSequence + 1} 공 소유자 O${selectedPlayer.number}`); } return; }
+2
View File
@@ -72,6 +72,8 @@ h1 { display:flex; align-items:center; gap:10px; color:#f4f2e8; font-size:17px;
@media(prefers-reduced-motion:reduce) { *,*::before,*::after { transition:none!important; } }
.mode-group button span { white-space: nowrap; }
.mode-group #remove-shoot-action { color:#a34b3f; }
.mode-group #remove-shoot-action span { white-space:normal; line-height:1.1; }
.account-gate { position:fixed; inset:0; z-index:50; display:grid; place-items:center; padding:24px; background:linear-gradient(145deg,#edf2eb,#f8f9f6 55%,#f5e8df); overflow:auto; }
.account-card { width:min(470px,100%); padding:32px; border:1px solid #dfe7dc; border-radius:20px; background:#fff; box-shadow:0 24px 70px #23342e18; }
.account-brand { display:flex; align-items:center; gap:10px; margin-bottom:34px; } .account-brand .brand-mark { width:40px; height:40px; } .account-brand strong { font-size:28px; letter-spacing:-.06em; line-height:1; } .account-brand strong span { font-weight:400; } .account-brand small { display:block; margin-top:5px; color:var(--muted); font-size:7px; letter-spacing:.14em; }
+1 -1
View File
@@ -107,7 +107,7 @@ export function editorLayout() {
<div class="canvas-header"><div><span class="section-kicker">THE PLAYGROUND</span><h1>전술 캔버스 <span id="canvas-step">01</span></h1></div><div class="view-group"><button id="tactical" class="view active">${icon('court')}작전판</button><button id="pov" class="view">${icon('eye')}선수 시점</button></div></div>
<div class="canvas-meta"><span><i class="live-dot"></i> HALF COURT</span><span>15 × 14 M <b>·</b> 5 ON 5</span></div>
<div id="board" class="board" aria-label="농구 작전 코트"></div><button id="cancel-gaze" hidden>시선 선택 취소</button>
<div class="board-tools"><div class="mode-group" aria-label="행동 도구"><button data-mode="start" class="mode start active">${icon('people')}<span>배치</span></button><span class="tool-divider"></span><button data-mode="move" class="mode">${icon('move')}<span>이동</span></button><button data-mode="pass" class="mode">${icon('pass')}<span>패스</span></button><button data-mode="screen" class="mode">${icon('screen')}<span>스크린</span></button><button id="shoot-action" class="mode">${icon('shoot')}<span>슛</span></button></div><div class="history-tools" aria-label="편집 기록"><button id="undo" class="history-button" type="button" aria-label="실행 취소" title="실행 취소 (Ctrl+Z)"><span class="history-glyph" aria-hidden="true">↶</span><span>실행 취소</span></button><button id="redo" class="history-button" type="button" aria-label="다시 실행" title="다시 실행 (Ctrl+Shift+Z)"><span class="history-glyph" aria-hidden="true">↷</span><span>다시 실행</span></button></div></div>
<div class="board-tools"><div class="mode-group" aria-label="행동 도구"><button data-mode="start" class="mode start active">${icon('people')}<span>배치</span></button><span class="tool-divider"></span><button data-mode="move" class="mode">${icon('move')}<span>이동</span></button><button data-mode="pass" class="mode">${icon('pass')}<span>패스</span></button><button data-mode="screen" class="mode">${icon('screen')}<span>스크린</span></button><button id="shoot-action" class="mode">${icon('shoot')}<span>슛</span></button><button id="remove-shoot-action" class="mode danger" type="button" hidden>${icon('shoot')}<span>슛 동작 제거</span></button></div><div class="history-tools" aria-label="편집 기록"><button id="undo" class="history-button" type="button" aria-label="실행 취소" title="실행 취소 (Ctrl+Z)"><span class="history-glyph" aria-hidden="true">↶</span><span>실행 취소</span></button><button id="redo" class="history-button" type="button" aria-label="다시 실행" title="다시 실행 (Ctrl+Shift+Z)"><span class="history-glyph" aria-hidden="true">↷</span><span>다시 실행</span></button></div></div>
<div class="focus-bottom-actions" aria-label="집중 편집 도구"><button id="toggle-focus-playback" type="button" aria-expanded="false" aria-controls="playback-deck" aria-label="단계·재생">단계</button><button id="toggle-focus-actions" type="button" aria-expanded="false" aria-controls="action-panel" aria-label="행동 편집">행동</button></div>
<div class="board-status"><span id="status" role="status">선수를 선택하고 첫 움직임을 만들어보세요.</span><span id="clock">00.0s</span></div>
</section>