summaryrefslogtreecommitdiff
path: root/web/src/grid.ts
blob: e849803d6e9df68c1ed1f53250e5a426c64917a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { produce } from "immer";
import renderGrid, { getRenderedCell } from "./components/grid/renderGrid";
import { getSelectionRange, GridSelection } from "./components/grid/selection";
import Ratio from "./math/Ratio";
import { Cell, Grid, renderedRowIndexToRef } from "./types";

export function getSelectedSubdivisionsCount(
  grid: Grid,
  selection: GridSelection,
): number | undefined {
  const renderedGrid = renderGrid(grid);

  const [startCellRef, endCellRef] = getSelectionRange(selection);
  const startCell = getRenderedCell(renderedGrid, startCellRef);
  const endCell = getRenderedCell(renderedGrid, endCellRef);

  if (!startCell || !endCell) throw new Error("Invalid cell refs");

  const startRenderedRowIndex = Math.min(
    startCell.renderedRowIndex,
    endCell.renderedRowIndex,
  );

  const endRenderedRowIndex = Math.max(
    startCell.renderedRowIndex,
    endCell.renderedRowIndex,
  );

  const startRatio = Ratio.min(startCell.startRatio, endCell.startRatio);
  const endRatio = Ratio.max(startCell.endRatio, endCell.endRatio);

  return Math.min(
    ...renderedGrid.renderedRows
      .slice(startRenderedRowIndex, endRenderedRowIndex + 1)
      .map((row) => {
        const startCellIndex = row.renderedCells.findIndex((cell) =>
          cell.startRatio.equals(startRatio),
        );

        const endCellIndex = row.renderedCells.findLastIndex((cell) =>
          cell.endRatio.equals(endRatio),
        );

        return endCellIndex - startCellIndex + 1;
      }),
  );
}

export function changeSelectedSubdivisions(
  grid: Grid,
  selection: GridSelection,
  subdivisions: number,
): Grid {
  const renderedGrid = renderGrid(grid);
  const [startCellRef, endCellRef] = getSelectionRange(selection);
  const startCell = getRenderedCell(renderedGrid, startCellRef);
  const endCell = getRenderedCell(renderedGrid, endCellRef);
  if (!startCell || !endCell) throw new Error("Invalid cell refs");

  const startRenderedRowIndex = Math.min(
    startCell.renderedRowIndex,
    endCell.renderedRowIndex,
  );

  const endRenderedRowIndex = Math.max(
    startCell.renderedRowIndex,
    endCell.renderedRowIndex,
  );

  const startRatio = Ratio.min(startCell.startRatio, endCell.startRatio);
  const endRatio = Ratio.max(startCell.endRatio, endCell.endRatio);
  const selectedWidthRatio = endRatio.subtract(startRatio);
  const widthRatio = selectedWidthRatio.divideRatio(
    Ratio.fromInteger(subdivisions),
  );

  return produce(grid, (draft) => {
    for (
      let renderedRowIndex = startRenderedRowIndex;
      renderedRowIndex <= endRenderedRowIndex;
      renderedRowIndex++
    ) {
      const renderedRow = renderedGrid.renderedRows[renderedRowIndex];

      const startCellIndex = renderedRow.renderedCells.findIndex((cell) =>
        cell.startRatio.equals(startRatio),
      );

      const endCellIndex = renderedRow.renderedCells.findLastIndex((cell) =>
        cell.endRatio.equals(endRatio),
      );

      const { partIndex, rowIndex } = renderedRowIndexToRef(
        grid,
        renderedRowIndex,
      );

      const row = draft.parts[partIndex].rows[rowIndex];
      const previousCells = row.cells.slice(0, startCellIndex);
      const nextCells = row.cells.slice(endCellIndex + 1);

      const newCells: Cell[] = Array.from({ length: subdivisions }, () => ({
        widthRatio,
      }));

      row.cells = [...previousCells, ...newCells, ...nextCells];
    }
  });
}