From: Carl Worth Date: Fri, 6 Mar 2026 21:44:58 +0000 (-0500) Subject: Several fixes for styling of the "blank modal" selector X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=f48f525ba6c1701f377bf83a65c883d499a7697f;p=lmno.games Several fixes for styling of the "blank modal" selector This is the modal dialog that pops up when the user clicks on a blank tile to select what letter it represents. The changes in this commit are: 1. Use the same appearance (referencing the same CSS class) for the tiles in this modal as for tiles in other contexts, (this replaces the previous custom styling which was white text on a light background and hard to read). 2. Restructure the modal so that it is centered over the grid rather than centered over the viewport (also constrain the rack to not be wider than the grid). 3. Fix a bug that resulted from the user cancelling a selection of a blank tile, (it was "returned" to the rack before being properly removed from the rack, resulting in two occurences of the same tile on the rack). See the reassign_true logic in this commit for the relevant fix. --- diff --git a/letterrip/letterrip.css b/letterrip/letterrip.css index 32458c1..653df63 100644 --- a/letterrip/letterrip.css +++ b/letterrip/letterrip.css @@ -92,6 +92,7 @@ justify-content: center; width: 44px; height: 44px; + color: #000; background: #f5e6c8; border: 2px solid #c9a96e; border-radius: 4px; @@ -156,10 +157,17 @@ } /* Board grid */ +.board-and-rack { + display: inline-flex; + flex-direction: column; + max-width: 100%; +} + .board-container { margin-bottom: 1em; overflow: auto; max-width: 100%; + --cell-size: 48px; } .board { @@ -168,10 +176,6 @@ background: #fff; } -.board-container { - --cell-size: 48px; -} - .cell { width: var(--cell-size); height: var(--cell-size); @@ -191,7 +195,12 @@ cursor: grab; } -/* Blank tile modal */ +/* Blank tile modal — positioned relative to the board-and-rack + container so it centers over the grid, not the full viewport. */ +.board-and-rack { + position: relative; +} + .blank-modal-overlay { position: fixed; top: 0; @@ -199,10 +208,19 @@ right: 0; bottom: 0; background: rgba(0,0,0,0.4); + z-index: 100; +} + +.blank-modal-anchor { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; display: flex; align-items: center; justify-content: center; - z-index: 100; + z-index: 101; } .blank-modal { @@ -227,15 +245,8 @@ .blank-modal .letter-btn { width: 38px; height: 38px; - border: 1px solid #999; - border-radius: 4px; - background: #f5e6c8; font-size: 18px; - font-weight: bold; cursor: pointer; - display: flex; - align-items: center; - justify-content: center; } .blank-modal .letter-btn:hover { diff --git a/letterrip/letterrip.jsx b/letterrip/letterrip.jsx index 4d58ca8..2fea89a 100644 --- a/letterrip/letterrip.jsx +++ b/letterrip/letterrip.jsx @@ -286,14 +286,15 @@ function PlayerList(props) { function BlankTileModal(props) { const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""); - return ( -
+ return [ +
, +
e.stopPropagation()}>

Choose a letter for blank tile

{letters.map(l => ( @@ -301,7 +302,7 @@ function BlankTileModal(props) {
- ); + ]; } function Tile(props) { @@ -836,7 +837,7 @@ class Game extends React.Component { if (cell && cell.isBlank) { const [r, c] = grid_key.split(",").map(Number); this.setState({ - blank_pending: { r, c, tileIndex: cell.tileIndex } + blank_pending: { r, c, tileIndex: cell.tileIndex, reassign: true } }); return; } @@ -964,29 +965,35 @@ class Game extends React.Component {
) : null, - state.joined ? this.render_board(analysis, invalid_cells, unconnected_cells) : null, - - state.joined ? this.render_rack(can_complete) : null, - - state.blank_pending ? ( - this.choose_blank_letter(l)} - onCancel={() => { - /* Put the blank tile back on the rack if cancelled. */ - const { tileIndex } = state.blank_pending; - const new_rack = [...state.rack, tileIndex]; - this.setState({ blank_pending: null, rack: new_rack }); - }} - /> - ) : null, - - state.joined && Object.keys(state.grid).length > 0 && !can_complete ? ( -
- {!analysis.all_valid ? "Some words are not valid." : - !analysis.all_connected ? "All tiles must be connected." : - state.rack.length > 0 ? "Place all your tiles to complete." : - null} + state.joined ? ( +
+ {this.render_board(analysis, invalid_cells, unconnected_cells)} + {this.render_rack(can_complete)} + {state.blank_pending ? ( + this.choose_blank_letter(l)} + onCancel={() => { + /* Put the blank tile back on the rack if cancelled, + * but only if it was freshly placed (not a reassignment + * of a blank already on the grid). */ + if (state.blank_pending.reassign) { + this.setState({ blank_pending: null }); + } else { + const { tileIndex } = state.blank_pending; + const new_rack = [...state.rack, tileIndex]; + this.setState({ blank_pending: null, rack: new_rack }); + } + }} + /> + ) : null} + {Object.keys(state.grid).length > 0 && !can_complete ? ( +
+ {!analysis.all_valid ? "Some words are not valid." : + !analysis.all_connected ? "All tiles must be connected." : + state.rack.length > 0 ? "Place all your tiles to complete." : + null} +
+ ) : null}
) : null ]; @@ -1036,7 +1043,7 @@ class Game extends React.Component { const cols = bounds.maxC - bounds.minC + 1; return ( -
+
{rows.flat()} @@ -1051,8 +1058,7 @@ class Game extends React.Component { if (rack_drag_over) className += " drag-over"; return ( -
this.on_rack_drag_over(e)} onDragLeave={(e) => this.on_rack_drag_leave(e)}