mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-27 01:25:36 -05:00
Table Designer: Move columns UI (#19154)
* format * added buttons and initial drag plugin * initial drag and drop working * add actions and taskbar * drag and drop bugfix and other changes * fix few issues * more changes * fix all move and insertion issues * PRcomments * fit and finish comments * remove dead code * bump sts * add style for object being dragged * add plugin to copyright filter * Add to eslintrc * fix drag contrast ratio * generalize logic for cell focus * demo feedback * feedback * add action state * feedback * remove unncecessary check * add move actions to context menu * change to const * fix bug with tables and fix drop color Co-authored-by: chgagnon <chgagnon@microsoft.com>
This commit is contained in:
175
src/sql/base/browser/ui/table/plugins/rowMoveManager.plugin.ts
Normal file
175
src/sql/base/browser/ui/table/plugins/rowMoveManager.plugin.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
// Adopted and converted to typescript from https://github.com/mleibman/SlickGrid/blob/gh-pages/plugins/slick.rowmovemanager.js
|
||||
// heavily modified
|
||||
|
||||
import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin';
|
||||
import { BaseClickableColumn, ClickableColumnOptions, IconColumnOptions } from 'sql/base/browser/ui/table/plugins/tableColumn';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
const defaultOptions: IRowMoveManagerOptions = {
|
||||
cancelEditOnDrag: false
|
||||
};
|
||||
|
||||
export interface IRowMoveManagerOptions extends IconColumnOptions, ClickableColumnOptions, Slick.Column<Slick.SlickData> {
|
||||
cancelEditOnDrag?: boolean;
|
||||
}
|
||||
|
||||
export interface RowMoveOnDragEventArgs {
|
||||
selectionProxy?: JQuery<HTMLElement>;
|
||||
guide?: JQuery<HTMLElement>;
|
||||
selectedRows?: number[];
|
||||
insertBefore?: number;
|
||||
canMove?: boolean;
|
||||
}
|
||||
|
||||
export interface RowMoveOnDragEventData {
|
||||
rows?: number[];
|
||||
insertBefore: number;
|
||||
}
|
||||
|
||||
// Wrapper interfaces for drag arguments to support selection
|
||||
export interface OnRowMoveDragInitEventArgs<T extends Slick.SlickData> extends Slick.OnDragInitEventArgs<T>, RowMoveOnDragEventArgs { }
|
||||
export interface OnRowMoveDragStartEventArgs<T extends Slick.SlickData> extends Slick.OnDragStartEventArgs<T>, RowMoveOnDragEventArgs { }
|
||||
export interface OnRowMoveDragEventArgs<T extends Slick.SlickData> extends Slick.OnDragEventArgs<T>, RowMoveOnDragEventArgs { }
|
||||
export interface OnRowMoveDragEndEventArgs<T extends Slick.SlickData> extends Slick.OnDragEndEventArgs<T>, RowMoveOnDragEventArgs { }
|
||||
|
||||
export class RowMoveManager<T extends Slick.SlickData> extends BaseClickableColumn<T> {
|
||||
|
||||
private _canvas: HTMLCanvasElement;
|
||||
private _dragging: boolean;
|
||||
|
||||
public onBeforeMoveRows: Slick.Event<any> = new Slick.Event();
|
||||
public onMoveRows: Slick.Event<any> = new Slick.Event();
|
||||
|
||||
constructor(private options: IRowMoveManagerOptions) {
|
||||
super(options);
|
||||
this.options = mixin(options, defaultOptions, false);
|
||||
}
|
||||
|
||||
public get definition(): Slick.Column<T> {
|
||||
return {
|
||||
id: this.options.id || this.options.title || this.options.field,
|
||||
width: this.options.width ?? 26,
|
||||
name: this.options.name,
|
||||
resizable: this.options.resizable,
|
||||
selectable: false,
|
||||
behavior: this.options.behavior,
|
||||
cssClass: this.options.iconCssClass
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public override init(grid: Slick.Grid<T>) {
|
||||
this._grid = grid;
|
||||
this._grid.setSelectionModel(new RowSelectionModel());
|
||||
this._canvas = this._grid.getCanvasNode();
|
||||
this._handler
|
||||
.subscribe(this._grid.onDragInit, (e: DOMEvent, data: OnRowMoveDragInitEventArgs<T>) => this.onDragInit(e as MouseEvent, data))
|
||||
.subscribe(this._grid.onDragStart, (e: DOMEvent, data: OnRowMoveDragStartEventArgs<T>) => this.onDragStart(e as MouseEvent, data))
|
||||
.subscribe(this._grid.onDrag, (e: DOMEvent, data: OnRowMoveDragEventArgs<T>) => this.onDrag(e as MouseEvent, data))
|
||||
.subscribe(this._grid.onDragEnd, (e: DOMEvent, data: OnRowMoveDragEndEventArgs<T>) => this.onDragEnd(e as MouseEvent, data));
|
||||
}
|
||||
|
||||
private onDragInit(e: MouseEvent, data: OnRowMoveDragInitEventArgs<T>) {
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
private onDragStart(e: MouseEvent, data: OnRowMoveDragStartEventArgs<T>) {
|
||||
const cell = this._grid.getCellFromEvent(e);
|
||||
const highlightStyle = {};
|
||||
const columns = this._grid.getColumns();
|
||||
highlightStyle[cell.row] = {};
|
||||
columns.forEach((c) => {
|
||||
highlightStyle[cell.row][c.id] = 'isDragging';
|
||||
});
|
||||
this._grid.setCellCssStyles('isDragging', highlightStyle);
|
||||
|
||||
if (this.options.cancelEditOnDrag && this._grid.getEditorLock().isActive()) {
|
||||
this._grid.getEditorLock().cancelCurrentEdit();
|
||||
}
|
||||
|
||||
if (this._grid.getEditorLock().isActive() || !/move|selectAndMove/.test(this._grid.getColumns()[cell.cell].behavior)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._dragging = true;
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
let selectedRows = this._grid.getSelectedRows();
|
||||
|
||||
if (selectedRows.length === 0 || jQuery.inArray(cell.row, selectedRows) === -1) {
|
||||
selectedRows = [cell.row];
|
||||
this._grid.setSelectedRows(selectedRows);
|
||||
}
|
||||
|
||||
const rowHeight = this._grid.getOptions().rowHeight;
|
||||
|
||||
data.selectedRows = selectedRows;
|
||||
|
||||
data.selectionProxy = jQuery('<div class="slick-reorder-proxy"/>')
|
||||
.css('position', 'absolute')
|
||||
.css('zIndex', '99999')
|
||||
.css('width', jQuery(this._canvas).innerWidth())
|
||||
.css('height', rowHeight * selectedRows.length)
|
||||
.appendTo(this._canvas);
|
||||
|
||||
data.guide = jQuery('<div class="slick-reorder-guide"/>')
|
||||
.css('position', 'absolute')
|
||||
.css('zIndex', '99998')
|
||||
.css('width', jQuery(this._canvas).innerWidth())
|
||||
.css('top', -1000)
|
||||
.appendTo(this._canvas);
|
||||
|
||||
data.insertBefore = -1;
|
||||
}
|
||||
|
||||
private onDrag(e: MouseEvent, data: OnRowMoveDragEventArgs<T>) {
|
||||
if (!this._dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
const top = e.pageY - jQuery(this._canvas).offset().top;
|
||||
data.selectionProxy.css('top', top - 5);
|
||||
|
||||
const insertBefore = Math.max(0, Math.min(Math.round(top / this._grid.getOptions().rowHeight), this._grid.getDataLength()));
|
||||
if (insertBefore !== data.insertBefore) {
|
||||
const eventData: RowMoveOnDragEventData = {
|
||||
'rows': data.selectedRows,
|
||||
'insertBefore': insertBefore
|
||||
};
|
||||
|
||||
if (this.onBeforeMoveRows.notify(eventData) === false) {
|
||||
data.guide.css('top', -1000);
|
||||
data.canMove = false;
|
||||
} else {
|
||||
data.guide.css('top', insertBefore * this._grid.getOptions().rowHeight);
|
||||
data.canMove = true;
|
||||
}
|
||||
|
||||
data.insertBefore = insertBefore;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private onDragEnd(e: MouseEvent, data: OnRowMoveDragEndEventArgs<T>) {
|
||||
if (!this._dragging) {
|
||||
return;
|
||||
}
|
||||
this._dragging = false;
|
||||
this._grid.removeCellCssStyles('isDragging');
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
data.guide.remove();
|
||||
data.selectionProxy.remove();
|
||||
|
||||
if (data.canMove) {
|
||||
const eventData: RowMoveOnDragEventData = {
|
||||
'rows': data.selectedRows,
|
||||
'insertBefore': data.insertBefore
|
||||
};
|
||||
this.onMoveRows.notify(eventData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,8 +25,8 @@ export interface ClickableColumnOptions {
|
||||
}
|
||||
|
||||
export abstract class BaseClickableColumn<T extends Slick.SlickData> implements Slick.Plugin<T>, TableColumn<T> {
|
||||
private _handler = new Slick.EventHandler();
|
||||
private _grid!: Slick.Grid<T>;
|
||||
protected _handler = new Slick.EventHandler();
|
||||
protected _grid!: Slick.Grid<T>;
|
||||
private _onClick = new Emitter<TableCellClickEventArgs<T>>();
|
||||
public onClick = this._onClick.event;
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@ export class Table<T extends Slick.SlickData> extends Widget implements IDisposa
|
||||
private _onColumnResize = new Emitter<void>();
|
||||
public readonly onColumnResize = this._onColumnResize.event;
|
||||
|
||||
private _onBlur = new Emitter<void>();
|
||||
public readonly onBlur = this._onBlur.event;
|
||||
|
||||
constructor(parent: HTMLElement, configuration?: ITableConfiguration<T>, options?: Slick.GridOptions<T>) {
|
||||
super();
|
||||
if (!configuration || !configuration.dataProvider || isArray(configuration.dataProvider)) {
|
||||
@@ -84,6 +87,7 @@ export class Table<T extends Slick.SlickData> extends Widget implements IDisposa
|
||||
clearTimeout(this._classChangeTimeout);
|
||||
this._classChangeTimeout = setTimeout(() => {
|
||||
this._container.classList.remove('focused');
|
||||
this._onBlur.fire();
|
||||
}, 100);
|
||||
}, true));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user