diff --git a/clients/web/src/game/components/board.rs b/clients/web/src/game/components/board.rs index 02bee6a..dda9ddc 100644 --- a/clients/web/src/game/components/board.rs +++ b/clients/web/src/game/components/board.rs @@ -312,13 +312,8 @@ pub fn Board( exit_field_test = |f| matches!(f, 1..=6); } - // Show a clickable exit sign outside the board when bearing off is possible. - let has_exit_move = valid_sequences - .iter() - .any(|(m1, m2)| m1.get_to() == 0 || m2.get_to() == 0); - let show_exit_btn = all_in_exit && is_move_stage && has_exit_move; - let seqs_exit_cls = valid_sequences.clone(); - let seqs_exit_click = valid_sequences.clone(); + // Sequences clone for the reactive exit button (show/hide + class + click). + let seqs_exit = valid_sequences.clone(); // `valid_sequences` is cloned per field (the Vec is small; Send-safe unlike Rc). let fields_from = |nums: &[u8], is_top_row: bool| -> Vec { @@ -624,70 +619,88 @@ pub fn Board( // Exit sign: circle+arrow outside the board, next to the last exit field. // White exits to the right (top-right quarter); Black exits to the left (top-left). - {show_exit_btn.then(|| { - let (pos_style, line_x1, line_x2, head_pts): (&str, &str, &str, &str) = - if is_white { - ( - "position:absolute;right:-60px;top:15px;width:50px;height:50px", - "10", "31", "23,17 32,25 23,33", - ) - } else { - ( - "position:absolute;left:-60px;top:15px;width:50px;height:50px", - "40", "19", "27,17 18,25 27,33", - ) - }; - view! { -
seqs_exit_cls.is_empty() - || valid_dests_for(&seqs_exit_cls, &staged, origin) - .iter() - .any(|&d| d == 0), - None => false, - }; - if active { "exit-btn exit-active" } else { "exit-btn" } - } - on:click=move |_| { - if !is_move_stage { return; } - let staged = staged_moves.get_untracked(); - if staged.len() >= 2 { return; } - let Some(origin) = selected_origin.get_untracked() else { - return; - }; - let valid = seqs_exit_click.is_empty() - || valid_dests_for(&seqs_exit_click, &staged, origin) - .iter() - .any(|&d| d == 0); - if valid { - staged_moves.update(|v| v.push((origin, 0))); - selected_origin.set(None); + {move || { + // Recompute on every staged_moves change: the exit button must appear + // even when the initial board has a checker outside the exit zone, + // because the first move can bring all checkers in (e.g. 15→21, 19→exit). + let staged = staged_moves.get(); + let show = is_move_stage && match staged.len() { + 0 => seqs_exit.iter().any(|(m1, m2)| m1.get_to() == 0 || m2.get_to() == 0), + 1 => { + let (f0, t0) = staged[0]; + seqs_exit.iter() + .filter(|(m1, _)| m1.get_from() as u8 == f0 && m1.get_to() as u8 == t0) + .any(|(_, m2)| m2.get_to() == 0) + } + _ => false, + }; + show.then(|| { + let seqs_exit_cls = seqs_exit.clone(); + let seqs_exit_click = seqs_exit.clone(); + let (pos_style, line_x1, line_x2, head_pts): (&str, &str, &str, &str) = + if is_white { + ( + "position:absolute;right:-60px;top:15px;width:50px;height:50px", + "10", "31", "23,17 32,25 23,33", + ) + } else { + ( + "position:absolute;left:-60px;top:15px;width:50px;height:50px", + "40", "19", "27,17 18,25 27,33", + ) + }; + view! { +
seqs_exit_cls.is_empty() + || valid_dests_for(&seqs_exit_cls, &staged, origin) + .iter() + .any(|&d| d == 0), + None => false, + }; + if active { "exit-btn exit-active" } else { "exit-btn" } } - } - > - - - - - -
- } - .into_any() - })} + on:click=move |_| { + if !is_move_stage { return; } + let staged = staged_moves.get_untracked(); + if staged.len() >= 2 { return; } + let Some(origin) = selected_origin.get_untracked() else { + return; + }; + let valid = seqs_exit_click.is_empty() + || valid_dests_for(&seqs_exit_click, &staged, origin) + .iter() + .any(|&d| d == 0); + if valid { + staged_moves.update(|v| v.push((origin, 0))); + selected_origin.set(None); + } + } + > + + + + + +
+ } + .into_any() + }) + }}
{label_bl}