add cells selection

This commit is contained in:
Sergey Krylov 2022-07-13 21:40:09 +05:00
parent b282490279
commit 2f78b7ec22
4 changed files with 100 additions and 36 deletions

View File

@ -62,7 +62,7 @@ export class Table extends ExcelComponent {
this.initTable();
this.$onEventFromObserver('formula:input', this.updateCurrentText);
this.$onEventFromObserver('formula:enter-press', () => this.selection.current.focus());
this.$onEventFromObserver('formula:enter-press', () => this.selection.$currentCell.focus());
this.$onEventFromObserver('toolbar:applyStyle', this.updateCurrentStyles);
this.$onEventFromObserver('toolbar:add-row', this.addNewRowHandler);
}
@ -126,12 +126,12 @@ export class Table extends ExcelComponent {
}
emitSelectCallback() {
this.$emitEventToObserver('table:select-cell', this.selection.current);
this.$emitEventToObserver('table:select-cell', this.selection.$currentCell);
const styles = this.selection.current?.getStyles(Object.keys(initialStyleState));
const styles = this.selection.$currentCell?.getStyles(Object.keys(initialStyleState));
this.dispatchToStore(changeCurrentStyles(styles));
this.dispatchToStore(changeCurrentText(this.selection.current.text));
this.dispatchToStore(changeCurrentText(this.selection.$currentCell.text));
}
async resizeTable(event: MouseEvent) {
@ -147,12 +147,12 @@ export class Table extends ExcelComponent {
}
updateCurrentText = (text: string) => {
this.selection.current.attr('data-value', text);
this.selection.current.text = parse(text);
this.selection.$currentCell.attr('data-value', text);
this.selection.$currentCell.text = parse(text);
this.dispatchToStore(actions.changeText({
text,
id: this.selection.current.data.id || startCellId,
id: this.selection.$currentCell.data.id || startCellId,
}));
};

View File

@ -4,17 +4,21 @@ import { getParamsFromCellId, startCellId } from 'components/table/table.functio
export class TableSelection {
static selectedClassName = 'selected';
private group: Dom[];
public current: Dom;
private selectedCellsGroup: Dom[];
// TODO remove focus, when selection
// $focusedCell and $currentCell can be different, f.e. if select cells with Shift key, currentCell
// will be last cell, focusedCell will start cell, and can be different from selectedCellsGroup first item
public $currentCell: Dom;
public $focusedCell: Dom;
public rootTable: Table;
constructor(rootTable: Table) {
this.group = [];
this.selectedCellsGroup = [];
this.rootTable = rootTable;
}
get selectedIds() {
return this.group.map(el => el.data.id);
return this.selectedCellsGroup.map(el => el.data.id);
}
// TODO make a focus manager
@ -32,12 +36,14 @@ export class TableSelection {
}
}
select($el: Dom) {
select($cell: Dom) {
this.clearSelection();
this.group = [$el];
this.current = $el;
$el.addClass(TableSelection.selectedClassName);
this.focusToCell($el);
this.selectedCellsGroup = [$cell];
this.$currentCell = $cell;
$cell.addClass(TableSelection.selectedClassName);
this.focusToCell($cell);
this.selectHeader($cell);
this.$focusedCell = $cell;
}
selectByCellId(cellID: { col: number, row: number }) {
@ -46,21 +52,19 @@ export class TableSelection {
if (col <= 0) col = 0;
if (row <= 0) row = 0;
this.clearSelection();
this.current = $(`[data-id="${row}:${col}"]`);
this.focusToCell(this.current);
this.current.addClass(TableSelection.selectedClassName);
this.select($(`[data-id="${row}:${col}"]`));
}
clearSelection() {
this.group.forEach(el => el?.removeClass(TableSelection.selectedClassName));
this.current?.removeClass(TableSelection.selectedClassName);
this.group = [];
this.selectedCellsGroup.forEach(el => el?.removeClass(TableSelection.selectedClassName));
this.$currentCell?.removeClass(TableSelection.selectedClassName);
this.clearHeaderSelection();
this.selectedCellsGroup = [];
}
selectTo($el: Dom) {
const startCellParams = getParamsFromCellId(this.current.data.id || startCellId);
const selectedCellParams = getParamsFromCellId($el.data.id || startCellId);
selectFromTo($from: Dom, $cell: Dom) {
const startCellParams = getParamsFromCellId($from.data.id || startCellId);
const selectedCellParams = getParamsFromCellId($cell.data.id || startCellId);
const startCol = Math.min(startCellParams.col, selectedCellParams.col);
const endCol = Math.max(startCellParams.col, selectedCellParams.col);
@ -71,8 +75,8 @@ export class TableSelection {
for (let row = startRow; row <= endRow; row++) {
for (let col = startCol; col <= endCol; col++) {
const $cell = $(`[data-id="${row}:${col}"]`);
this.addCellToSelection($cell);
const $target = $(`[data-id="${row}:${col}"]`);
this.addCellToSelection($target);
}
}
}
@ -83,11 +87,56 @@ export class TableSelection {
}
addCellToSelection($cell: Dom) {
this.group.push($cell);
this.selectedCellsGroup.push($cell);
$cell.addClass(TableSelection.selectedClassName);
this.selectHeader($cell);
}
addGroupToSelectionById(cellID: { col: number, row: number }) {
if (this.selectedCellsGroup.length === 1) {
this.$focusedCell = this.selectedCellsGroup[0];
}
const { col, row } = cellID;
const $cell = $(`[data-id="${row}:${col}"]`);
const $lastCell = this.selectedCellsGroup[this.selectedCellsGroup.length - 1];
if (!$lastCell.isExist) {
this.select($lastCell);
return;
}
this.selectFromTo(this.$focusedCell, $cell);
this.$currentCell = $cell;
}
applyStyle(style: CSSStyleDeclaration) {
this.group.forEach(el => el.css(style));
this.selectedCellsGroup.forEach(el => el.css(style));
}
selectHeader($cell: Dom) {
if (!$cell) return;
const { headerCol, headerRow } = this.findHeadOfCell($cell);
headerRow.addClass(TableSelection.selectedClassName);
headerCol.addClass(TableSelection.selectedClassName);
}
clearHeaderSelection() {
this.rootTable.$root.findAll('[data-header]').forEach(header => {
header.classList.remove(TableSelection.selectedClassName);
});
}
findHeadOfCell($cell: Dom): { headerRow: Dom, headerCol: Dom } {
const headerRow = $cell.closest('[data-row]').find("[data-header='row']");
const headerCol = this.rootTable.$root.find(`[data-col="${$cell.data.id?.split(':')[1]}"]`);
return { headerRow, headerCol };
}
isCellInSelection($cell: Dom) {
return this.selectedCellsGroup.includes($cell);
}
}

View File

@ -29,7 +29,7 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl
const target = $(event.target);
if (isCell(event)) {
if (event.shiftKey) selection.selectTo(target);
if (event.shiftKey) selection.selectFromTo(selection.$currentCell, target);
else if (event.ctrlKey) selection.addCellToSelection(target);
else selection.select(target);
} else {
@ -63,10 +63,10 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl
'Tab',
];
if (!selection?.current || !handleKeys.includes(key)) return;
if (!selection?.$currentCell || !handleKeys.includes(key)) return;
// If something goes wrong, go to start line
const currentCellId = selection.current.data.id || startCellId;
const currentCellId = selection.$currentCell.data.id || startCellId;
let { row, col } = getParamsFromCellId(currentCellId);
switch (key) {
@ -104,13 +104,14 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl
default: break;
}
selection.selectByCellId({ row, col });
if (event.shiftKey) selection.addGroupToSelectionById({ row, col });
else selection.selectByCellId({ row, col });
}
function onMouseOverHandler() {
if (selection.current.$el) {
if (selection.$currentCell.$el) {
// TODO find event type
selection.selectTo($((event as any).toElement));
selection.selectFromTo(selection.$currentCell, $((event as any).toElement));
}
}
}

View File

@ -93,8 +93,22 @@
}
[data-header="row"] {
cursor: e-resize;
&.selected {
border-right: 2px solid #3c74ff;
}
}
[data-header="col"] {
cursor: n-resize;
&.selected {
border-bottom: 2px solid #3c74ff;
}
}
[data-header="col"].selected, [data-header="row"].selected {
font-weight: bold;
background: rgba(60, 116, 255, 0.4);
}
}