mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-20 01:25:37 -05:00
Better cell selection (#6914)
* Better cell selection * Explicit return type and undefined assignment * More complex copy/paste * Get TS to be less mad at me * Remove EoL * Fail safe if statement * strict null check
This commit is contained in:
@@ -28,6 +28,7 @@ export interface ICellRangeSelectorOptions {
|
||||
export interface ICellRangeSelector<T> extends Slick.Plugin<T> {
|
||||
onCellRangeSelected: Slick.Event<Slick.Range>;
|
||||
onBeforeCellRangeSelected: Slick.Event<Slick.Cell>;
|
||||
onAppendCellRangeSelected: Slick.Event<Slick.Range>;
|
||||
}
|
||||
|
||||
export interface ICellRangeDecorator {
|
||||
@@ -45,6 +46,7 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
|
||||
|
||||
public onBeforeCellRangeSelected = new Slick.Event<Slick.Cell>();
|
||||
public onCellRangeSelected = new Slick.Event<Slick.Range>();
|
||||
public onAppendCellRangeSelected = new Slick.Event<Slick.Range>();
|
||||
|
||||
constructor(private options: ICellRangeSelectorOptions) {
|
||||
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
|
||||
@@ -138,11 +140,18 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
|
||||
if (!dd || !dd.range || !dd.range.start || !dd.range.end) {
|
||||
return;
|
||||
}
|
||||
this.onCellRangeSelected.notify(new Slick.Range(
|
||||
|
||||
let newRange = new Slick.Range(
|
||||
dd.range.start.row,
|
||||
dd.range.start.cell,
|
||||
dd.range.end.row,
|
||||
dd.range.end.cell
|
||||
));
|
||||
);
|
||||
|
||||
if (e.ctrlKey) {
|
||||
this.onAppendCellRangeSelected.notify(newRange);
|
||||
} else {
|
||||
this.onCellRangeSelected.notify(newRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +36,14 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
|
||||
|
||||
public init(grid: Slick.Grid<T>) {
|
||||
this.grid = grid;
|
||||
this._handler.subscribe(this.grid.onActiveCellChanged, (e: Event, args: Slick.OnActiveCellChangedEventArgs<T>) => this.handleActiveCellChange(e, args));
|
||||
this._handler.subscribe(this.grid.onClick, (e: MouseEvent, args: Slick.OnActiveCellChangedEventArgs<T>) => this.handleActiveCellChange(e, args));
|
||||
this._handler.subscribe(this.grid.onKeyDown, (e: KeyboardEvent) => this.handleKeyDown(e));
|
||||
this._handler.subscribe(this.grid.onClick, (e: MouseEvent, args: Slick.OnClickEventArgs<T>) => this.handleIndividualCellSelection(e, args));
|
||||
this._handler.subscribe(this.grid.onHeaderClick, (e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) => this.handleHeaderClick(e, args));
|
||||
this.grid.registerPlugin(this.selector);
|
||||
this._handler.subscribe(this.selector.onCellRangeSelected, (e: Event, range: Slick.Range) => this.handleCellRangeSelected(e, range));
|
||||
this._handler.subscribe(this.selector.onCellRangeSelected, (e: Event, range: Slick.Range) => this.handleCellRangeSelected(e, range, false));
|
||||
this._handler.subscribe(this.selector.onAppendCellRangeSelected, (e: Event, range: Slick.Range) => this.handleCellRangeSelected(e, range, true));
|
||||
|
||||
this._handler.subscribe(this.selector.onBeforeCellRangeSelected, (e: Event, cell: Slick.Cell) => this.handleBeforeCellRangeSelected(e, cell));
|
||||
}
|
||||
|
||||
@@ -87,13 +90,18 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
|
||||
return true;
|
||||
}
|
||||
|
||||
private handleCellRangeSelected(e: Event, range: Slick.Range) {
|
||||
private handleCellRangeSelected(e: Event, range: Slick.Range, append: boolean) {
|
||||
this.grid.setActiveCell(range.fromRow, range.fromCell, false, false, true);
|
||||
this.setSelectedRanges([range]);
|
||||
|
||||
if (append) {
|
||||
this.setSelectedRanges(this.insertIntoSelections(this.getSelectedRanges(), range));
|
||||
} else {
|
||||
this.setSelectedRanges([range]);
|
||||
}
|
||||
}
|
||||
|
||||
private handleActiveCellChange(e: Event, args: Slick.OnActiveCellChangedEventArgs<T>) {
|
||||
if (this.options.selectActiveCell && !isUndefinedOrNull(args.row) && !isUndefinedOrNull(args.cell)) {
|
||||
private handleActiveCellChange(e: MouseEvent, args: Slick.OnActiveCellChangedEventArgs<T>) {
|
||||
if (this.options.selectActiveCell && !isUndefinedOrNull(args.row) && !isUndefinedOrNull(args.cell) && !e.ctrlKey) {
|
||||
this.setSelectedRanges([new Slick.Range(args.row, args.cell)]);
|
||||
} else if (!this.options.selectActiveCell) {
|
||||
// clear the previous selection once the cell changes
|
||||
@@ -118,6 +126,119 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* DO NOT CALL THIS DIRECTLY - GO THROUGH INSERT INTO SELECTIONS
|
||||
*
|
||||
*/
|
||||
private mergeSelections(ranges: Array<Slick.Range>, range: Slick.Range) {
|
||||
// New ranges selection
|
||||
let newRanges: Array<Slick.Range> = [];
|
||||
|
||||
// Have we handled this value
|
||||
let handled = false;
|
||||
for (let current of ranges) {
|
||||
// We've already processed everything. Add everything left back to the list.
|
||||
if (handled) {
|
||||
newRanges.push(current);
|
||||
continue;
|
||||
}
|
||||
let newRange: Slick.Range | undefined = undefined;
|
||||
|
||||
// if the ranges are the same.
|
||||
if (current.fromRow === range.fromRow &&
|
||||
current.fromCell === range.fromCell &&
|
||||
current.toRow === range.toRow &&
|
||||
current.toCell === range.toCell) {
|
||||
// If we're actually not going to handle it during this loop
|
||||
// this region will be added with the handled boolean check
|
||||
continue;
|
||||
}
|
||||
|
||||
// Rows are the same - horizontal merging of the selection area
|
||||
if (current.fromRow === range.fromRow && current.toRow === range.toRow) {
|
||||
// Check if the new region is adjacent to the old selection group
|
||||
if (range.toCell + 1 === current.fromCell || range.fromCell - 1 === current.toCell) {
|
||||
handled = true;
|
||||
let fromCell = Math.min(range.fromCell, current.fromCell, range.toCell, current.toCell);
|
||||
let toCell = Math.max(range.fromCell, current.fromCell, range.toCell, current.toCell);
|
||||
newRange = new Slick.Range(range.fromRow, fromCell, range.toRow, toCell);
|
||||
}
|
||||
// Cells are the same - vertical merging of the selection area
|
||||
} else if (current.fromCell === range.fromCell && current.toCell === range.toCell) {
|
||||
// Check if the new region is adjacent to the old selection group
|
||||
if (range.toRow + 1 === current.fromRow || range.fromRow - 1 === current.toRow) {
|
||||
handled = true;
|
||||
let fromRow = Math.min(range.fromRow, current.fromRow, range.fromRow, current.fromRow);
|
||||
let toRow = Math.max(range.toRow, current.toRow, range.toRow, current.toRow);
|
||||
newRange = new Slick.Range(fromRow, range.fromCell, toRow, range.toCell);
|
||||
}
|
||||
}
|
||||
|
||||
if (newRange) {
|
||||
newRanges.push(newRange);
|
||||
} else {
|
||||
newRanges.push(current);
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
newRanges.push(range);
|
||||
}
|
||||
|
||||
return {
|
||||
newRanges,
|
||||
handled
|
||||
};
|
||||
}
|
||||
|
||||
private insertIntoSelections(ranges: Array<Slick.Range>, range: Slick.Range): Array<Slick.Range> {
|
||||
let result = this.mergeSelections(ranges, range);
|
||||
let newRanges = result.newRanges;
|
||||
|
||||
// Keep merging the rows until we stop having changes
|
||||
let i = 0;
|
||||
while (true) {
|
||||
if (i++ > 10000) {
|
||||
console.error('InsertIntoSelection infinite loop: Report this error on github');
|
||||
break;
|
||||
}
|
||||
let shouldContinue = false;
|
||||
for (let current of newRanges) {
|
||||
result = this.mergeSelections(newRanges, current);
|
||||
if (result.handled) {
|
||||
shouldContinue = true;
|
||||
newRanges = result.newRanges;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldContinue) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return newRanges;
|
||||
}
|
||||
|
||||
private handleIndividualCellSelection(e: MouseEvent, args: Slick.OnClickEventArgs<T>) {
|
||||
if (!e.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ranges: Array<Slick.Range>;
|
||||
|
||||
ranges = this.getSelectedRanges();
|
||||
ranges = this.insertIntoSelections(ranges, new Slick.Range(args.row, args.cell));
|
||||
|
||||
this.grid.setActiveCell(args.row, args.cell);
|
||||
this.setSelectedRanges(ranges);
|
||||
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
private handleKeyDown(e: KeyboardEvent) {
|
||||
/***
|
||||
* Кey codes
|
||||
|
||||
Reference in New Issue
Block a user