/* The grid starts as the largest square that fits the viewport width
* (capped at 10x10) and only grows when a tile is placed in one of
- * the outermost rows/columns. */
+ * the outermost rows/columns. When the grid expands in one direction,
+ * empty rows/columns on the opposite side are trimmed (keeping at
+ * least one empty row/column adjacent to the nearest tile). The grid
+ * never shrinks below the initial size. */
function initial_grid_size() {
const cell_size = get_cell_size() + 2; /* +2 for 1px border each side */
const available = window.innerWidth - 32; /* rough page padding */
return Math.max(4, Math.min(10, fit));
}
-function grid_bounds(grid, prev_bounds) {
+function grid_bounds(grid) {
+ const min_size = initial_grid_size();
const keys = Object.keys(grid);
+
if (keys.length === 0) {
- const half = Math.floor(initial_grid_size() / 2);
+ const half = Math.floor(min_size / 2);
return { minR: -half, maxR: half - 1, minC: -half, maxC: half - 1 };
}
if (c > tileMaxC) tileMaxC = c;
}
- /* Start from previous bounds (or initial size if none). */
- let minR, maxR, minC, maxC;
- if (prev_bounds) {
- minR = prev_bounds.minR;
- maxR = prev_bounds.maxR;
- minC = prev_bounds.minC;
- maxC = prev_bounds.maxC;
- } else {
- const half = Math.floor(initial_grid_size() / 2);
- minR = Math.min(-half, tileMinR - 1);
- maxR = Math.max(half - 1, tileMaxR + 1);
- minC = Math.min(-half, tileMinC - 1);
- maxC = Math.max(half - 1, tileMaxC + 1);
- }
-
- /* Expand only if a tile touches the edge (ensure 1 empty row/col). */
- if (tileMinR <= minR) minR = tileMinR - 1;
- if (tileMaxR >= maxR) maxR = tileMaxR + 1;
- if (tileMinC <= minC) minC = tileMinC - 1;
- if (tileMaxC >= maxC) maxC = tileMaxC + 1;
-
- return { minR, maxR, minC, maxC };
+ /* The grid must include all tiles plus 1 empty row/column on each
+ * side, and must be at least min_size in each dimension. */
+ const tile_rows = tileMaxR - tileMinR + 1 + 2; /* +2 for padding */
+ const tile_cols = tileMaxC - tileMinC + 1 + 2;
+ const rows = Math.max(min_size, tile_rows);
+ const cols = Math.max(min_size, tile_cols);
+
+ /* Center the extra space around the tiles. */
+ const extra_rows = rows - tile_rows;
+ const extra_cols = cols - tile_cols;
+ const pad_top = Math.floor(extra_rows / 2);
+ const pad_left = Math.floor(extra_cols / 2);
+
+ return {
+ minR: tileMinR - 1 - pad_top,
+ maxR: tileMinR - 1 - pad_top + rows - 1,
+ minC: tileMinC - 1 - pad_left,
+ maxC: tileMinC - 1 - pad_left + cols - 1
+ };
}
/*********************************************************
drag_source: null,
drag_over_cell: null,
rack_drag_over: false,
- grid_bounds: null,
selected: null
};
}
render_board(analysis, invalid_cells, unconnected_cells) {
const { grid, drag_over_cell, drag_source, selected } = this.state;
- const bounds = grid_bounds(grid, this.state.grid_bounds);
- /* Store bounds so the grid only grows, never shrinks. */
- if (JSON.stringify(bounds) !== JSON.stringify(this.state.grid_bounds)) {
- setTimeout(() => this.setState({ grid_bounds: bounds }), 0);
- }
+ const bounds = grid_bounds(grid);
const rows = [];
for (let r = bounds.minR; r <= bounds.maxR; r++) {