Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
269 lines
6.7 KiB
TypeScript
269 lines
6.7 KiB
TypeScript
import { ref } from 'vue';
|
|
|
|
import type { CellRange } from '../types';
|
|
import {
|
|
normalizeRange,
|
|
rangesContainCell,
|
|
} from '../commands/FillRangeCommand';
|
|
|
|
export interface SelectionModifiers {
|
|
shift?: boolean;
|
|
ctrl?: boolean;
|
|
}
|
|
|
|
function fullRowRange(row: number, maxCol: number): CellRange {
|
|
return normalizeRange({
|
|
startRow: row,
|
|
endRow: row,
|
|
startCol: 0,
|
|
endCol: maxCol,
|
|
});
|
|
}
|
|
|
|
function fullColRange(col: number, maxRow: number): CellRange {
|
|
return normalizeRange({
|
|
startRow: 0,
|
|
endRow: maxRow,
|
|
startCol: col,
|
|
endCol: col,
|
|
});
|
|
}
|
|
|
|
function rowRange(r0: number, r1: number, maxCol: number): CellRange {
|
|
return normalizeRange({
|
|
startRow: r0,
|
|
endRow: r1,
|
|
startCol: 0,
|
|
endCol: maxCol,
|
|
});
|
|
}
|
|
|
|
function colRange(c0: number, c1: number, maxRow: number): CellRange {
|
|
return normalizeRange({
|
|
startRow: 0,
|
|
endRow: maxRow,
|
|
startCol: c0,
|
|
endCol: c1,
|
|
});
|
|
}
|
|
|
|
function isFullRowRange(range: CellRange, maxCol: number): boolean {
|
|
const r = normalizeRange(range);
|
|
return r.startCol === 0 && r.endCol === maxCol;
|
|
}
|
|
|
|
function isFullColRange(range: CellRange, maxRow: number): boolean {
|
|
const r = normalizeRange(range);
|
|
return r.startRow === 0 && r.endRow === maxRow;
|
|
}
|
|
|
|
function rangeContainsRow(range: CellRange, row: number, maxCol: number): boolean {
|
|
const r = normalizeRange(range);
|
|
return isFullRowRange(r, maxCol) && row >= r.startRow && row <= r.endRow;
|
|
}
|
|
|
|
function rangeContainsCol(range: CellRange, col: number, maxRow: number): boolean {
|
|
const r = normalizeRange(range);
|
|
return isFullColRange(r, maxRow) && col >= r.startCol && col <= r.endCol;
|
|
}
|
|
|
|
export function useSelection() {
|
|
const ranges = ref<CellRange[]>([]);
|
|
const anchor = ref<{ row: number; col: number } | null>(null);
|
|
const activeCell = ref<{ row: number; col: number } | null>(null);
|
|
const isFillDragging = ref(false);
|
|
|
|
/** @deprecated use ranges */
|
|
const selection = ranges;
|
|
|
|
function selectCell(row: number, col: number) {
|
|
activeCell.value = { row, col };
|
|
anchor.value = { row, col };
|
|
ranges.value = [
|
|
{ startRow: row, startCol: col, endRow: row, endCol: col },
|
|
];
|
|
}
|
|
|
|
function extendSelection(row: number, col: number) {
|
|
if (!anchor.value) {
|
|
selectCell(row, col);
|
|
return;
|
|
}
|
|
ranges.value = [
|
|
{
|
|
startRow: anchor.value.row,
|
|
startCol: anchor.value.col,
|
|
endRow: row,
|
|
endCol: col,
|
|
},
|
|
];
|
|
activeCell.value = { row, col };
|
|
}
|
|
|
|
function selectRange(range: CellRange) {
|
|
const normalized = normalizeRange(range);
|
|
ranges.value = [normalized];
|
|
activeCell.value = {
|
|
row: normalized.startRow,
|
|
col: normalized.startCol,
|
|
};
|
|
anchor.value = { ...activeCell.value };
|
|
}
|
|
|
|
function getSelectionRanges(): CellRange[] {
|
|
return ranges.value.map((r) => normalizeRange(r));
|
|
}
|
|
|
|
function getNormalizedSelection(): CellRange | null {
|
|
const primary = getPrimaryRange();
|
|
return primary;
|
|
}
|
|
|
|
function getPrimaryRange(): CellRange | null {
|
|
if (!ranges.value.length) return null;
|
|
if (!activeCell.value) return normalizeRange(ranges.value[0]!);
|
|
const { row, col } = activeCell.value;
|
|
for (const range of ranges.value) {
|
|
const r = normalizeRange(range);
|
|
if (
|
|
row >= r.startRow &&
|
|
row <= r.endRow &&
|
|
col >= r.startCol &&
|
|
col <= r.endCol
|
|
) {
|
|
return r;
|
|
}
|
|
}
|
|
return normalizeRange(ranges.value[ranges.value.length - 1]!);
|
|
}
|
|
|
|
function containsCell(row: number, col: number): boolean {
|
|
return rangesContainCell(ranges.value, row, col);
|
|
}
|
|
|
|
function selectAll(maxRow: number, maxCol: number) {
|
|
const range = normalizeRange({
|
|
startRow: 0,
|
|
endRow: maxRow,
|
|
startCol: 0,
|
|
endCol: maxCol,
|
|
});
|
|
ranges.value = [range];
|
|
activeCell.value = { row: 0, col: 0 };
|
|
anchor.value = { row: 0, col: 0 };
|
|
}
|
|
|
|
function selectEntireRow(
|
|
row: number,
|
|
maxCol: number,
|
|
opts: SelectionModifiers = {},
|
|
) {
|
|
const { shift, ctrl } = opts;
|
|
if (ctrl) {
|
|
const next = [...ranges.value];
|
|
const idx = next.findIndex((r) => rangeContainsRow(r, row, maxCol));
|
|
if (idx >= 0) {
|
|
next.splice(idx, 1);
|
|
} else {
|
|
next.push(fullRowRange(row, maxCol));
|
|
}
|
|
ranges.value = next.length ? next : [];
|
|
if (ranges.value.length) {
|
|
activeCell.value = { row, col: 0 };
|
|
if (!shift) anchor.value = { row, col: 0 };
|
|
} else {
|
|
activeCell.value = null;
|
|
}
|
|
return;
|
|
}
|
|
if (shift && anchor.value) {
|
|
const range = rowRange(anchor.value.row, row, maxCol);
|
|
ranges.value = [range];
|
|
activeCell.value = { row, col: 0 };
|
|
return;
|
|
}
|
|
ranges.value = [fullRowRange(row, maxCol)];
|
|
activeCell.value = { row, col: 0 };
|
|
anchor.value = { row, col: 0 };
|
|
}
|
|
|
|
function selectEntireColumn(
|
|
col: number,
|
|
maxRow: number,
|
|
opts: SelectionModifiers = {},
|
|
) {
|
|
const { shift, ctrl } = opts;
|
|
if (ctrl) {
|
|
const next = [...ranges.value];
|
|
const idx = next.findIndex((r) => rangeContainsCol(r, col, maxRow));
|
|
if (idx >= 0) {
|
|
next.splice(idx, 1);
|
|
} else {
|
|
next.push(fullColRange(col, maxRow));
|
|
}
|
|
ranges.value = next.length ? next : [];
|
|
if (ranges.value.length) {
|
|
activeCell.value = { row: 0, col };
|
|
if (!shift) anchor.value = { row: 0, col };
|
|
} else {
|
|
activeCell.value = null;
|
|
}
|
|
return;
|
|
}
|
|
if (shift && anchor.value) {
|
|
const range = colRange(anchor.value.col, col, maxRow);
|
|
ranges.value = [range];
|
|
activeCell.value = { row: 0, col };
|
|
return;
|
|
}
|
|
ranges.value = [fullColRange(col, maxRow)];
|
|
activeCell.value = { row: 0, col };
|
|
anchor.value = { row: 0, col };
|
|
}
|
|
|
|
function extendRowSelection(row: number, maxCol: number) {
|
|
if (!anchor.value) {
|
|
selectEntireRow(row, maxCol);
|
|
return;
|
|
}
|
|
ranges.value = [rowRange(anchor.value.row, row, maxCol)];
|
|
activeCell.value = { row, col: 0 };
|
|
}
|
|
|
|
function extendColumnSelection(col: number, maxRow: number) {
|
|
if (!anchor.value) {
|
|
selectEntireColumn(col, maxRow);
|
|
return;
|
|
}
|
|
ranges.value = [colRange(anchor.value.col, col, maxRow)];
|
|
activeCell.value = { row: 0, col };
|
|
}
|
|
|
|
function clearSelection() {
|
|
ranges.value = [];
|
|
anchor.value = null;
|
|
activeCell.value = null;
|
|
}
|
|
|
|
return {
|
|
ranges,
|
|
selection,
|
|
anchor,
|
|
activeCell,
|
|
isFillDragging,
|
|
selectCell,
|
|
extendSelection,
|
|
selectRange,
|
|
selectAll,
|
|
selectEntireRow,
|
|
selectEntireColumn,
|
|
extendRowSelection,
|
|
extendColumnSelection,
|
|
getSelectionRanges,
|
|
getNormalizedSelection,
|
|
getPrimaryRange,
|
|
containsCell,
|
|
clearSelection,
|
|
};
|
|
}
|